diff --git a/compiler/crates/relay-codegen/src/build_ast.rs b/compiler/crates/relay-codegen/src/build_ast.rs index 3d0e162846bc8..7b6dd4f8b24bd 100644 --- a/compiler/crates/relay-codegen/src/build_ast.rs +++ b/compiler/crates/relay-codegen/src/build_ast.rs @@ -70,6 +70,7 @@ use relay_transforms::INTERNAL_METADATA_DIRECTIVE; use relay_transforms::RELAY_ACTOR_CHANGE_DIRECTIVE_FOR_CODEGEN; use relay_transforms::RESOLVER_BELONGS_TO_BASE_SCHEMA_DIRECTIVE; use relay_transforms::TYPE_DISCRIMINATOR_DIRECTIVE_NAME; +use schema::Field; use schema::SDLSchema; use schema::Schema; use schema::Type; @@ -241,34 +242,20 @@ pub fn build_resolvers_schema( } fields.push(ObjectEntry { key: field.name.item, - value: Primitive::Key(ast_builder.intern(Ast::Object(object! { - resolver_function: Primitive::JSModuleDependency(JSModuleDependency { - path: project_config.js_module_import_identifier( - artifact_path, - &PathBuf::from(import_path.lookup()), - ), - import_name: ModuleImportName::Named { - name: import_name, - import_as: Some(resolver_import_alias(object.name.item.0, field.name.item)) - }, - }), - root_fragment: match get_resolver_fragment_dependency_name(field) { - Some(name) => { - let definition_name = WithLocation::new( - field.name.location, - get_normalization_operation_name(name.0).intern(), - ); - Primitive::JSModuleDependency(JSModuleDependency { - path: project_config.js_module_import_identifier( - artifact_path, - &project_config.artifact_path_for_definition(definition_name), - ), - import_name: ModuleImportName::Default(definition_name.item), - }) - }, - None => Primitive::SkippableNull, + value: Primitive::Key(build_resolver_info( + ast_builder, + project_config, + artifact_path, + field, + import_path, + ModuleImportName::Named { + name: import_name, + import_as: Some(resolver_import_alias( + object.name.item.0, + field.name.item, + )), }, - }))), + )), }); } } @@ -283,6 +270,41 @@ pub fn build_resolvers_schema( ast_builder.intern(Ast::Object(map)) } +fn build_resolver_info( + ast_builder: &mut AstBuilder, + project_config: &ProjectConfig, + artifact_path: &PathBuf, + field: &Field, + import_path: StringKey, + import_name: ModuleImportName, +) -> AstKey { + ast_builder.intern(Ast::Object(object! { + resolver_function: Primitive::JSModuleDependency(JSModuleDependency { + path: project_config.js_module_import_identifier( + artifact_path, + &PathBuf::from(import_path.lookup()), + ), + import_name, + }), + root_fragment: match get_resolver_fragment_dependency_name(field) { + Some(name) => { + let definition_name = WithLocation::new( + field.name.location, + get_normalization_operation_name(name.0).intern(), + ); + Primitive::JSModuleDependency(JSModuleDependency { + path: project_config.js_module_import_identifier( + artifact_path, + &project_config.artifact_path_for_definition(definition_name), + ), + import_name: ModuleImportName::Default(definition_name.item), + }) + } + None => Primitive::SkippableNull, + }, + })) +} + pub struct CodegenBuilder<'schema, 'builder, 'config> { connection_constants: ConnectionConstants, schema: &'schema SDLSchema, @@ -742,7 +764,12 @@ impl<'schema, 'builder, 'config> CodegenBuilder<'schema, 'builder, 'config> { resolver_metadata: &RelayResolverMetadata, inline_fragment: Option, ) -> Primitive { - if self.project_config.resolvers_schema_module.is_some() { + if self + .project_config + .resolvers_schema_module + .as_ref() + .is_some_and(|config| config.apply_to_normalization_ast) + { self.build_normalization_relay_resolver_execution_time_for_worker(resolver_metadata) } else if self .project_config @@ -811,26 +838,29 @@ impl<'schema, 'builder, 'config> CodegenBuilder<'schema, 'builder, 'config> { .normalization_ast_should_have_is_output_type_true(); let variable_name = resolver_metadata.generate_local_resolver_name(self.schema); - let resolver_js_module = JSModuleDependency { - path: self.project_config.js_module_import_identifier( - &self - .project_config - .artifact_path_for_definition(self.definition_source_location), - &PathBuf::from(resolver_metadata.import_path.lookup()), - ), - import_name: match resolver_metadata.import_name { + let artifact_path = &self + .project_config + .artifact_path_for_definition(self.definition_source_location); + let kind = if resolver_metadata.live { + CODEGEN_CONSTANTS.relay_live_resolver + } else { + CODEGEN_CONSTANTS.relay_resolver + }; + let resolver_info = build_resolver_info( + self.ast_builder, + self.project_config, + artifact_path, + self.schema.field(resolver_metadata.field_id), + resolver_metadata.import_path, + match resolver_metadata.import_name { Some(name) => ModuleImportName::Named { name, import_as: Some(variable_name), }, None => ModuleImportName::Default(variable_name), }, - }; - let kind = if resolver_metadata.live { - CODEGEN_CONSTANTS.relay_live_resolver - } else { - CODEGEN_CONSTANTS.relay_resolver - }; + ); + Primitive::Key(self.object(object! { name: Primitive::String(field_name), args: match args { @@ -849,7 +879,7 @@ impl<'schema, 'builder, 'config> CodegenBuilder<'schema, 'builder, 'config> { } }, is_output_type: Primitive::Bool(is_output_type), - resolver_module: Primitive::JSModuleDependency(resolver_js_module), + resolver_info: Primitive::Key(resolver_info), })) } @@ -871,16 +901,6 @@ impl<'schema, 'builder, 'config> CodegenBuilder<'schema, 'builder, 'config> { }; let variable_name = resolver_metadata.generate_local_resolver_name(self.schema); - let resolver_js_module = ResolverModuleReference { - field_type, - resolver_function_name: match resolver_metadata.import_name { - Some(name) => ModuleImportName::Named { - name, - import_as: Some(variable_name), - }, - None => ModuleImportName::Default(variable_name), - }, - }; let kind = if resolver_metadata.live { CODEGEN_CONSTANTS.relay_live_resolver } else { @@ -904,7 +924,16 @@ impl<'schema, 'builder, 'config> CodegenBuilder<'schema, 'builder, 'config> { } }, is_output_type: Primitive::Bool(is_output_type), - resolver_module: Primitive::ResolverModuleReference(resolver_js_module), + resolver_reference: Primitive::ResolverModuleReference(ResolverModuleReference { + field_type, + resolver_function_name: match resolver_metadata.import_name { + Some(name) => ModuleImportName::Named { + name, + import_as: Some(variable_name), + }, + None => ModuleImportName::Default(variable_name), + }, + }), })) } diff --git a/compiler/crates/relay-codegen/src/constants.rs b/compiler/crates/relay-codegen/src/constants.rs index 48ec1efc25bb6..572883727b624 100644 --- a/compiler/crates/relay-codegen/src/constants.rs +++ b/compiler/crates/relay-codegen/src/constants.rs @@ -102,7 +102,9 @@ pub struct CodegenConstants { pub request: StringKey, pub required_field: StringKey, pub resolver_function: StringKey, + pub resolver_info: StringKey, pub resolver_module: StringKey, + pub resolver_reference: StringKey, pub root_argument: StringKey, pub root_fragment: StringKey, pub scalar_field: StringKey, @@ -215,7 +217,9 @@ lazy_static! { request: "Request".intern(), required_field: "RequiredField".intern(), resolver_function: "resolverFunction".intern(), + resolver_info: "resolverInfo".intern(), resolver_module: "resolverModule".intern(), + resolver_reference: "resolverReference".intern(), root_argument: "RootArgument".intern(), root_fragment: "rootFragment".intern(), scalar_field: "ScalarField".intern(), diff --git a/compiler/crates/relay-compiler/src/build_project/generate_artifacts.rs b/compiler/crates/relay-compiler/src/build_project/generate_artifacts.rs index 877db399f750d..e4d5d7dece703 100644 --- a/compiler/crates/relay-compiler/src/build_project/generate_artifacts.rs +++ b/compiler/crates/relay-compiler/src/build_project/generate_artifacts.rs @@ -189,7 +189,7 @@ pub fn generate_artifacts( })) .chain( match project_config.resolvers_schema_module { - Some(ResolversSchemaModuleConfig { ref path }) => + Some(ResolversSchemaModuleConfig { ref path , .. }) => vec![ generate_resolvers_schema_module_artifact(path.clone()) ], diff --git a/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/relay-resolver-weak-object-normalization-ast.expected b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/relay-resolver-weak-object-normalization-ast.expected index 4a561c4d723f8..ad01ebb580787 100644 --- a/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/relay-resolver-weak-object-normalization-ast.expected +++ b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/relay-resolver-weak-object-normalization-ast.expected @@ -105,7 +105,10 @@ extend type Query { "kind": "RelayResolver", "storageKey": null, "isOutputType": true, - "resolverModule": require('ClientTypeResolver') + "resolverInfo": { + "resolverFunction": require('ClientTypeResolver'), + "rootFragment": null + } }, "linkedField": { "alias": null, diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolvers_schema_module.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolvers_schema_module.expected index 71b35d59d37e9..c2b8493ef82ef 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolvers_schema_module.expected +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolvers_schema_module.expected @@ -65,7 +65,7 @@ module.exports = schema_resolvers; //- __generated__/UserFooFragment$normalization.graphql.js /** - * SignedSource<> + * SignedSource<<79043ca4ab6c3a1b54e21b82211b593c>> * @flow * @lightSyntaxTransform * @nogrep @@ -94,7 +94,10 @@ var node/*: NormalizationSplitOperation*/ = { "kind": "RelayResolver", "storageKey": null, "isOutputType": true, - "resolverModule": { resolverFunctionName: "bar", fieldType: "User" } + "resolverInfo": { + "resolverFunction": require('User_bar').bar, + "rootFragment": null + } } ] } diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolvers_schema_module_apply_to_normalization_ast.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolvers_schema_module_apply_to_normalization_ast.expected new file mode 100644 index 0000000000000..572dee402bfc1 --- /dev/null +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolvers_schema_module_apply_to_normalization_ast.expected @@ -0,0 +1,170 @@ +==================================== INPUT ==================================== +//- User_foo.js +/** + * @RelayResolver User.foo: RelayResolverValue + * @rootFragment UserFooFragment + */ +graphql`fragment UserFooFragment on User { + bar +}` + +//- User_bar.js +/** + * @RelayResolver User.bar: RelayResolverValue + */ + +//- relay.config.json +{ + "language": "flow", + "jsModuleFormat": "haste", + "schema": "schema.graphql", + "featureFlags": { + "enable_relay_resolver_transform": true, + "enable_resolver_normalization_ast": true + }, + "resolversSchemaModule": { + "applyToNormalizationAst": true, + "path": "__generated__/ResolversSchemaModule.js" + } +} + +//- schema.graphql +type User { name: String } +==================================== OUTPUT =================================== +//- __generated__/ResolversSchemaModule.js +/** + * SignedSource<> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { SchemaResolvers } from 'ReactiveQueryExecutor'; +import type { ResolverFunction, NormalizationSplitOperation } from 'relay-runtime'; + +*/ + +var schema_resolvers/*: SchemaResolvers*/ = { + "User": { + "bar": { + "resolverFunction": require('User_bar').bar, + "rootFragment": null + }, + "foo": { + "resolverFunction": require('User_foo').foo, + "rootFragment": require('UserFooFragment$normalization.graphql') + } + } +}; + +module.exports = schema_resolvers; + +//- __generated__/UserFooFragment$normalization.graphql.js +/** + * SignedSource<<0dc5f1959c406808557590206bee9173>> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { NormalizationSplitOperation } from 'relay-runtime'; + +*/ + +var node/*: NormalizationSplitOperation*/ = { + "kind": "SplitOperation", + "metadata": {}, + "name": "UserFooFragment$normalization", + "selections": [ + { + "kind": "ClientExtension", + "selections": [ + { + "name": "bar", + "args": null, + "kind": "RelayResolver", + "storageKey": null, + "isOutputType": true, + "resolverReference": { resolverFunctionName: "bar", fieldType: "User" } + } + ] + } + ] +}; + +(node/*: any*/).hash = "285ee53d00b8def775c9e1ed756743bf"; + +module.exports = node; + +//- __generated__/UserFooFragment.graphql.js +/** + * SignedSource<<8cc1f9903984d3c06d796d4524cf1c23>> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { Fragment, ReaderFragment } from 'relay-runtime'; +import type { FragmentType } from "relay-runtime"; +import {bar as userBarResolverType} from "User_bar"; +// Type assertion validating that `userBarResolverType` resolver is correctly implemented. +// A type error here indicates that the type signature of the resolver module is incorrect. +(userBarResolverType: () => ?mixed); +declare export opaque type UserFooFragment$fragmentType: FragmentType; +export type UserFooFragment$data = {| + +bar: ?ReturnType, + +$fragmentType: UserFooFragment$fragmentType, +|}; +export type UserFooFragment$key = { + +$data?: UserFooFragment$data, + +$fragmentSpreads: UserFooFragment$fragmentType, + ... +}; +*/ + +var node/*: ReaderFragment*/ = { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": null, + "name": "UserFooFragment", + "selections": [ + { + "kind": "ClientExtension", + "selections": [ + { + "alias": null, + "args": null, + "fragment": null, + "kind": "RelayResolver", + "name": "bar", + "resolverModule": require('User_bar').bar, + "path": "bar" + } + ] + } + ], + "type": "User", + "abstractKey": null +}; + +(node/*: any*/).hash = "285ee53d00b8def775c9e1ed756743bf"; + +module.exports = ((node/*: any*/)/*: Fragment< + UserFooFragment$fragmentType, + UserFooFragment$data, +>*/); diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolvers_schema_module_apply_to_normalization_ast.input b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolvers_schema_module_apply_to_normalization_ast.input new file mode 100644 index 0000000000000..c7eda265f10db --- /dev/null +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolvers_schema_module_apply_to_normalization_ast.input @@ -0,0 +1,31 @@ +//- User_foo.js +/** + * @RelayResolver User.foo: RelayResolverValue + * @rootFragment UserFooFragment + */ +graphql`fragment UserFooFragment on User { + bar +}` + +//- User_bar.js +/** + * @RelayResolver User.bar: RelayResolverValue + */ + +//- relay.config.json +{ + "language": "flow", + "jsModuleFormat": "haste", + "schema": "schema.graphql", + "featureFlags": { + "enable_relay_resolver_transform": true, + "enable_resolver_normalization_ast": true + }, + "resolversSchemaModule": { + "applyToNormalizationAst": true, + "path": "__generated__/ResolversSchemaModule.js" + } +} + +//- schema.graphql +type User { name: String } diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration_test.rs b/compiler/crates/relay-compiler/tests/relay_compiler_integration_test.rs index ed218f3a3b364..88a9c6cca5bd4 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration_test.rs +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration_test.rs @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<0d99ccbe5aade8069b9aa9abad3aba77>> + * @generated SignedSource<<81e6648f89d4ad26d959f72f1eaab040>> */ mod relay_compiler_integration; @@ -89,6 +89,13 @@ async fn resolvers_schema_module() { test_fixture(transform_fixture, file!(), "resolvers_schema_module.input", "relay_compiler_integration/fixtures/resolvers_schema_module.expected", input, expected).await; } +#[tokio::test] +async fn resolvers_schema_module_apply_to_normalization_ast() { + let input = include_str!("relay_compiler_integration/fixtures/resolvers_schema_module_apply_to_normalization_ast.input"); + let expected = include_str!("relay_compiler_integration/fixtures/resolvers_schema_module_apply_to_normalization_ast.expected"); + test_fixture(transform_fixture, file!(), "resolvers_schema_module_apply_to_normalization_ast.input", "relay_compiler_integration/fixtures/resolvers_schema_module_apply_to_normalization_ast.expected", input, expected).await; +} + #[tokio::test] async fn simple_fragment() { let input = include_str!("relay_compiler_integration/fixtures/simple_fragment.input"); diff --git a/compiler/crates/relay-config/src/resolvers_schema_module_config.rs b/compiler/crates/relay-config/src/resolvers_schema_module_config.rs index 46ebf2fc77bbe..39576387b6afa 100644 --- a/compiler/crates/relay-config/src/resolvers_schema_module_config.rs +++ b/compiler/crates/relay-config/src/resolvers_schema_module_config.rs @@ -14,6 +14,8 @@ use serde::Serialize; #[derive(Default, Serialize, Deserialize, Debug)] #[serde(deny_unknown_fields, rename_all = "camelCase")] pub struct ResolversSchemaModuleConfig { + #[serde(default)] + pub apply_to_normalization_ast: bool, #[serde(default)] pub path: PathBuf, } diff --git a/packages/relay-runtime/util/NormalizationNode.js b/packages/relay-runtime/util/NormalizationNode.js index 8daa85f302430..c03c8069f00f2 100644 --- a/packages/relay-runtime/util/NormalizationNode.js +++ b/packages/relay-runtime/util/NormalizationNode.js @@ -11,7 +11,7 @@ 'use strict'; -import type {ResolverModule} from './ReaderNode'; +import type {ResolverFunction, ResolverModule} from './ReaderNode'; import type {ConcreteRequest} from './RelayConcreteNode'; import type {JSResourceReference} from 'JSResourceReference'; @@ -165,11 +165,21 @@ export type NormalizationScalarField = { +storageKey?: ?string, }; -export type ResolverModuleReference = { +export type ResolverReference = { +fieldType: string, +resolverFunctionName: string, }; +export type ResolverInfo = { + +resolverFunction: ResolverFunction, + +rootFragment?: ?NormalizationSplitOperation, +}; + +type ResolverData = + | {+resolverModule?: ResolverModule | ResolverReference} + | {+resolverReference?: ResolverReference} + | {+resolverInfo?: ResolverInfo}; + export type NormalizationResolverField = { +kind: 'RelayResolver', +name: string, @@ -177,7 +187,7 @@ export type NormalizationResolverField = { +fragment?: ?NormalizationInlineFragment, +storageKey: ?string, +isOutputType: boolean, - +resolverModule?: ResolverModule | ResolverModuleReference, + ...ResolverData, }; export type NormalizationLiveResolverField = { @@ -187,7 +197,7 @@ export type NormalizationLiveResolverField = { +fragment?: ?NormalizationInlineFragment, +storageKey: ?string, +isOutputType: boolean, - +resolverModule?: ResolverModule | ResolverModuleReference, + ...ResolverData, }; export type NormalizationClientEdgeToClientObject = {