diff --git a/packages/relay b/packages/relay
index bf46d770..72f4958b 160000
--- a/packages/relay
+++ b/packages/relay
@@ -1 +1 @@
-Subproject commit bf46d77025c120e0405f1aff66a4791669029eda
+Subproject commit 72f4958b940166a3c9ee9ffd21a0e1d232cb1797
diff --git a/packages/rescript-relay/__tests__/TestRelayUserResolver2.res b/packages/rescript-relay/__tests__/TestRelayUserResolver2.res
new file mode 100644
index 00000000..39f4cf0b
--- /dev/null
+++ b/packages/rescript-relay/__tests__/TestRelayUserResolver2.res
@@ -0,0 +1,17 @@
+module Fragment = %relay(`
+ fragment TestRelayUserResolver2 on User {
+ firstName
+ lastName
+ }
+`)
+
+/**
+ * @RelayResolver User.fullName2(maxLength: Int!): String
+ * @rootFragment TestRelayUserResolver2
+ *
+ * A users full name 2.
+ */
+let fullName2 = (user, args) => {
+ let user = Fragment.readResolverFragment(user)
+ `${user.firstName} ${user.lastName}`->Js.String2.slice(~from=0, ~to_=args.maxLength)
+}
diff --git a/packages/rescript-relay/__tests__/Test_relayResolvers-tests.js b/packages/rescript-relay/__tests__/Test_relayResolvers-tests.js
index d3ec080a..75e7fc6e 100644
--- a/packages/rescript-relay/__tests__/Test_relayResolvers-tests.js
+++ b/packages/rescript-relay/__tests__/Test_relayResolvers-tests.js
@@ -20,6 +20,6 @@ describe("Relay Resolvers", () => {
});
t.render(test_relayResolvers());
- await t.screen.findByText("First Last is online");
+ await t.screen.findByText("First Last Fi is online");
});
});
diff --git a/packages/rescript-relay/__tests__/Test_relayResolvers.res b/packages/rescript-relay/__tests__/Test_relayResolvers.res
index b341fd92..760abcfc 100644
--- a/packages/rescript-relay/__tests__/Test_relayResolvers.res
+++ b/packages/rescript-relay/__tests__/Test_relayResolvers.res
@@ -12,6 +12,7 @@ module Fragment = %relay(`
fragment TestRelayResolvers_user on User {
isOnline
fullName
+ fullName2(maxLength: 2)
}
`)
@@ -23,8 +24,8 @@ module Test = {
{switch data {
- | {isOnline: Some(isOnline), fullName: Some(fullName)} =>
- React.string(`${fullName} is ${isOnline ? "online" : "offline"}`)
+ | {isOnline: Some(isOnline), fullName: Some(fullName), fullName2: Some(fullName2)} =>
+ React.string(`${fullName} ${fullName2} is ${isOnline ? "online" : "offline"}`)
| _ => React.string("-")
}}
diff --git a/packages/rescript-relay/__tests__/__generated__/TestRelayResolversQuery_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestRelayResolversQuery_graphql.res
index 5ac7478f..eb4dedd1 100644
--- a/packages/rescript-relay/__tests__/__generated__/TestRelayResolversQuery_graphql.res
+++ b/packages/rescript-relay/__tests__/__generated__/TestRelayResolversQuery_graphql.res
@@ -81,7 +81,29 @@ type relayOperationNode
type operationType = RescriptRelay.queryNode
-let node: operationType = %raw(json` {
+let node: operationType = %raw(json` (function(){
+var v0 = {
+ "kind": "InlineFragment",
+ "selections": [
+ {
+ "alias": null,
+ "args": null,
+ "kind": "ScalarField",
+ "name": "firstName",
+ "storageKey": null
+ },
+ {
+ "alias": null,
+ "args": null,
+ "kind": "ScalarField",
+ "name": "lastName",
+ "storageKey": null
+ }
+ ],
+ "type": "User",
+ "abstractKey": null
+};
+return {
"fragment": {
"argumentDefinitions": [],
"kind": "Fragment",
@@ -132,31 +154,25 @@ let node: operationType = %raw(json` {
{
"name": "fullName",
"args": null,
- "fragment": {
- "kind": "InlineFragment",
- "selections": [
- {
- "alias": null,
- "args": null,
- "kind": "ScalarField",
- "name": "firstName",
- "storageKey": null
- },
- {
- "alias": null,
- "args": null,
- "kind": "ScalarField",
- "name": "lastName",
- "storageKey": null
- }
- ],
- "type": "User",
- "abstractKey": null
- },
+ "fragment": (v0/*: any*/),
"kind": "RelayResolver",
"storageKey": null,
"isOutputType": true
},
+ {
+ "name": "fullName2",
+ "args": [
+ {
+ "kind": "Literal",
+ "name": "maxLength",
+ "value": 2
+ }
+ ],
+ "fragment": (v0/*: any*/),
+ "kind": "RelayResolver",
+ "storageKey": "fullName2(maxLength:2)",
+ "isOutputType": true
+ },
{
"alias": null,
"args": null,
@@ -170,14 +186,15 @@ let node: operationType = %raw(json` {
]
},
"params": {
- "cacheID": "f1931c3327fbb966e46860c167aab18d",
+ "cacheID": "fc699ed75840880919543f5be5cbe28e",
"id": null,
"metadata": {},
"name": "TestRelayResolversQuery",
"operationKind": "query",
- "text": "query TestRelayResolversQuery {\n loggedInUser {\n ...TestRelayResolvers_user\n id\n }\n}\n\nfragment TestRelayResolvers_user on User {\n isOnline\n ...TestRelayUserResolver\n}\n\nfragment TestRelayUserResolver on User {\n firstName\n lastName\n}\n"
+ "text": "query TestRelayResolversQuery {\n loggedInUser {\n ...TestRelayResolvers_user\n id\n }\n}\n\nfragment TestRelayResolvers_user on User {\n isOnline\n ...TestRelayUserResolver\n ...TestRelayUserResolver2\n}\n\nfragment TestRelayUserResolver on User {\n firstName\n lastName\n}\n\nfragment TestRelayUserResolver2 on User {\n firstName\n lastName\n}\n"
}
-} `)
+};
+})() `)
@live let load: (
~environment: RescriptRelay.Environment.t,
diff --git a/packages/rescript-relay/__tests__/__generated__/TestRelayResolvers_user_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestRelayResolvers_user_graphql.res
index 2765b07a..80f15e58 100644
--- a/packages/rescript-relay/__tests__/__generated__/TestRelayResolvers_user_graphql.res
+++ b/packages/rescript-relay/__tests__/__generated__/TestRelayResolvers_user_graphql.res
@@ -6,6 +6,7 @@ module Types = {
type fragment = {
fullName: option,
+ fullName2: option,
isOnline: option,
}
}
@@ -41,9 +42,10 @@ type relayOperationNode
type operationType = RescriptRelay.fragmentNode
-%%private(let makeNode = (resolverDataInjector, rescript_module_TestRelayUserResolver_fullName): operationType => {
+%%private(let makeNode = (resolverDataInjector, rescript_module_TestRelayUserResolver_fullName, rescript_module_TestRelayUserResolver2_fullName2): operationType => {
ignore(resolverDataInjector)
ignore(rescript_module_TestRelayUserResolver_fullName)
+ ignore(rescript_module_TestRelayUserResolver2_fullName2)
%raw(json`{
"argumentDefinitions": [],
"kind": "Fragment",
@@ -69,11 +71,30 @@ type operationType = RescriptRelay.fragmentNode
"name": "fullName",
"resolverModule": rescript_module_TestRelayUserResolver_fullName,
"path": "fullName"
+ },
+ {
+ "alias": null,
+ "args": [
+ {
+ "kind": "Literal",
+ "name": "maxLength",
+ "value": 2
+ }
+ ],
+ "fragment": {
+ "args": null,
+ "kind": "FragmentSpread",
+ "name": "TestRelayUserResolver2"
+ },
+ "kind": "RelayResolver",
+ "name": "fullName2",
+ "resolverModule": rescript_module_TestRelayUserResolver2_fullName2,
+ "path": "fullName2"
}
],
"type": "User",
"abstractKey": null
}`)
})
-let node: operationType = makeNode(RescriptRelay.resolverDataInjector, TestRelayUserResolver.fullName)
+let node: operationType = makeNode(RescriptRelay.resolverDataInjector, TestRelayUserResolver.fullName, TestRelayUserResolver2.fullName2)
diff --git a/packages/rescript-relay/__tests__/__generated__/TestRelayUserResolver2_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestRelayUserResolver2_graphql.res
new file mode 100644
index 00000000..14f438e8
--- /dev/null
+++ b/packages/rescript-relay/__tests__/__generated__/TestRelayUserResolver2_graphql.res
@@ -0,0 +1,68 @@
+/* @sourceLoc TestRelayUserResolver2.res */
+/* @generated */
+%%raw("/* @generated */")
+module Types = {
+ @@warning("-30")
+
+ type fragment = {
+ firstName: string,
+ lastName: string,
+ }
+}
+
+module Internal = {
+ @live
+ type fragmentRaw
+ @live
+ let fragmentConverter: Js.Dict.t>> = %raw(
+ json`{}`
+ )
+ @live
+ let fragmentConverterMap = ()
+ @live
+ let convertFragment = v => v->RescriptRelay.convertObj(
+ fragmentConverter,
+ fragmentConverterMap,
+ Js.undefined
+ )
+}
+
+type t
+type fragmentRef
+external getFragmentRef:
+ RescriptRelay.fragmentRefs<[> | #TestRelayUserResolver2]> => fragmentRef = "%identity"
+
+module Utils = {
+ @@warning("-33")
+ open Types
+}
+
+type relayOperationNode
+type operationType = RescriptRelay.fragmentNode
+
+
+let node: operationType = %raw(json` {
+ "argumentDefinitions": [],
+ "kind": "Fragment",
+ "metadata": null,
+ "name": "TestRelayUserResolver2",
+ "selections": [
+ {
+ "alias": null,
+ "args": null,
+ "kind": "ScalarField",
+ "name": "firstName",
+ "storageKey": null
+ },
+ {
+ "alias": null,
+ "args": null,
+ "kind": "ScalarField",
+ "name": "lastName",
+ "storageKey": null
+ }
+ ],
+ "type": "User",
+ "abstractKey": null
+} `)
+
diff --git a/packages/rescript-relay/__tests__/__generated__/User_relayResolvers_graphql.res b/packages/rescript-relay/__tests__/__generated__/User_relayResolvers_graphql.res
index 062c28e9..c23bfe98 100644
--- a/packages/rescript-relay/__tests__/__generated__/User_relayResolvers_graphql.res
+++ b/packages/rescript-relay/__tests__/__generated__/User_relayResolvers_graphql.res
@@ -1,5 +1,10 @@
/* @generated */
@@warning("-30")
-type fullNameResolver = () => RescriptRelay.any
+type fullNameResolver = (RescriptRelay.resolverFragmentRefs<[#TestRelayUserResolver]>, ) => RescriptRelay.any
+
+type fullName2ResolverArgs = {
+ maxLength: int,
+}
+type fullName2Resolver = (RescriptRelay.resolverFragmentRefs<[#TestRelayUserResolver2]>, fullName2ResolverArgs) => string
diff --git a/packages/rescript-relay/rescript-relay-ppx/library/Fragment.ml b/packages/rescript-relay/rescript-relay-ppx/library/Fragment.ml
index 4d3c4edc..d3226c48 100644
--- a/packages/rescript-relay/rescript-relay-ppx/library/Fragment.ml
+++ b/packages/rescript-relay/rescript-relay-ppx/library/Fragment.ml
@@ -2,7 +2,7 @@ open Ppxlib
open Util
let make ~loc ~moduleName ~refetchableQueryName ~extractedConnectionInfo
- ~hasInlineDirective =
+ ~hasInlineDirective ~isPlural =
let typeFromGeneratedModule = makeTypeAccessor ~loc ~moduleName in
let valFromGeneratedModule = makeExprAccessor ~loc ~moduleName in
let moduleIdentFromGeneratedModule = makeModuleIdent ~loc ~moduleName in
@@ -35,7 +35,8 @@ let make ~loc ~moduleName ~refetchableQueryName ~extractedConnectionInfo
~node:[%e valFromGeneratedModule ["node"]]];
[%stri
let useOpt fRef :
- [%t typeFromGeneratedModule ["Types"; "fragment"]] option =
+ [%t typeFromGeneratedModule ["Types"; "fragment"]] option
+ =
RescriptRelay_Fragment.useFragmentOpt ~convertFragment
~fRef:
(match fRef with
@@ -45,6 +46,34 @@ let make ~loc ~moduleName ~refetchableQueryName ~extractedConnectionInfo
|. [%e valFromGeneratedModule ["getFragmentRef"]])
| None -> None)
~node:[%e valFromGeneratedModule ["node"]]];
+ (if isPlural then
+ [%stri
+ let readResolverFragment fRef :
+ [%t typeFromGeneratedModule ["Types"; "fragment"]] =
+ let fRef =
+ fRef
+ |. RescriptRelay_Internal
+ .internal_resolverFragmentRefsToFragmentRefsPlural
+ in
+ RescriptRelay_Fragment.read ~convertFragment
+ ~fRef:
+ (fRef
+ |. [%e valFromGeneratedModule ["getFragmentRef"]])
+ ~node:[%e valFromGeneratedModule ["node"]]]
+ else
+ [%stri
+ let readResolverFragment fRef :
+ [%t typeFromGeneratedModule ["Types"; "fragment"]] =
+ let fRef =
+ fRef
+ |. RescriptRelay_Internal
+ .internal_resolverFragmentRefsToFragmentRefs
+ in
+ RescriptRelay_Fragment.read ~convertFragment
+ ~fRef:
+ (fRef
+ |. [%e valFromGeneratedModule ["getFragmentRef"]])
+ ~node:[%e valFromGeneratedModule ["node"]]]);
]
| true -> []);
(match hasInlineDirective with
@@ -73,8 +102,7 @@ let make ~loc ~moduleName ~refetchableQueryName ~extractedConnectionInfo
let makeRefetchVariables =
[%e
valFromRefetchableModule ["Types"; "makeRefetchVariables"]]
- [@@ocaml.doc "A helper to make refetch variables. "]
- [@@live]];
+ [@@ocaml.doc "A helper to make refetch variables. "] [@@live]];
[%stri
let convertRefetchVariables :
[%t
diff --git a/packages/rescript-relay/rescript-relay-ppx/library/RescriptRelayPpxLibrary.ml b/packages/rescript-relay/rescript-relay-ppx/library/RescriptRelayPpxLibrary.ml
index c1222117..ce5a7d77 100644
--- a/packages/rescript-relay/rescript-relay-ppx/library/RescriptRelayPpxLibrary.ml
+++ b/packages/rescript-relay/rescript-relay-ppx/library/RescriptRelayPpxLibrary.ml
@@ -26,6 +26,7 @@ let commonExtension =
~refetchableQueryName
~extractedConnectionInfo:(op |> extractFragmentConnectionInfo ~loc)
~hasInlineDirective:(op |> fragmentHasInlineDirective ~loc)
+ ~isPlural:(op |> fragmentIsPlural ~loc)
~loc
| Operation {optype = Query} ->
if Util.queryIsUpdatable op then
diff --git a/packages/rescript-relay/rescript-relay-ppx/library/Util.ml b/packages/rescript-relay/rescript-relay-ppx/library/Util.ml
index 5e169f19..2e643016 100755
--- a/packages/rescript-relay/rescript-relay-ppx/library/Util.ml
+++ b/packages/rescript-relay/rescript-relay-ppx/library/Util.ml
@@ -113,6 +113,16 @@ let fragmentHasInlineDirective ~loc op =
|> List.exists (fun (directive : Graphql_parser.directive) ->
directive.name = "inline")
| _ -> false
+
+let fragmentIsPlural ~loc op =
+ match op with
+ | Graphql_parser.Fragment {name = _; directives} ->
+ directives
+ |> List.exists (fun (directive : Graphql_parser.directive) ->
+ directive.name = "relay"
+ && directive.arguments
+ |> List.exists (fun (n, v) -> n = "plural" && v = `Bool true))
+ | _ -> false
let getGraphQLModuleName opName = String.capitalize_ascii opName ^ "_graphql"
let rec longidentFromStrings = function
diff --git a/packages/rescript-relay/src/RescriptRelay.res b/packages/rescript-relay/src/RescriptRelay.res
index af714d3d..98d076ef 100644
--- a/packages/rescript-relay/src/RescriptRelay.res
+++ b/packages/rescript-relay/src/RescriptRelay.res
@@ -9,6 +9,7 @@ type mutationNode<'node>
type subscriptionNode<'node>
type fragmentRefs<'fragments>
+type resolverFragmentRefs<'fragments>
type updatableFragmentRefs<'fragments>
type dataId
diff --git a/packages/rescript-relay/src/RescriptRelay.resi b/packages/rescript-relay/src/RescriptRelay.resi
index f0ce7e94..ec44a54d 100644
--- a/packages/rescript-relay/src/RescriptRelay.resi
+++ b/packages/rescript-relay/src/RescriptRelay.resi
@@ -30,6 +30,9 @@ type subscriptionNode<'node>
/**This type shows all of the fragments that has been spread on this particular object.*/
type fragmentRefs<'fragments>
+/**This type shows the Relay resolver fragment that has been spread on this particular object.*/
+type resolverFragmentRefs<'fragments>
+
/**This type shows all of the updatable fragments that has been spread on this particular object.*/
type updatableFragmentRefs<'fragments>
diff --git a/packages/rescript-relay/src/RescriptRelay_Fragment.res b/packages/rescript-relay/src/RescriptRelay_Fragment.res
index 6476aa92..5e334ac6 100644
--- a/packages/rescript-relay/src/RescriptRelay_Fragment.res
+++ b/packages/rescript-relay/src/RescriptRelay_Fragment.res
@@ -52,6 +52,16 @@ let readInlineData = (~node, ~convertFragment: 'fragment => 'fragment, ~fRef) =>
(readInlineData_(node, fRef)->convertFragment)
}
+@module("relay-runtime/lib/store/ResolverFragments")
+external read_: (fragmentNode<'node>, 'fragmentRef) => 'fragment = "readFragment"
+
+let read = (~node, ~convertFragment: 'fragment => 'fragment, ~fRef) => {
+ /** This lets you get the data for this fragment _outside \
+ of React's render_. Useful for letting functions with \
+ with fragments too, for things like logging etc.*/
+ (read_(node, fRef)->convertFragment)
+}
+
type refetchableFnOpts = {
fetchPolicy?: fetchPolicy,
onComplete?: Js.Nullable.t => unit,
diff --git a/packages/rescript-relay/src/RescriptRelay_Fragment.resi b/packages/rescript-relay/src/RescriptRelay_Fragment.resi
index 809ab9c3..7b6a3cb2 100644
--- a/packages/rescript-relay/src/RescriptRelay_Fragment.resi
+++ b/packages/rescript-relay/src/RescriptRelay_Fragment.resi
@@ -18,6 +18,12 @@ let readInlineData: (
~fRef: 'b,
) => 'fragment
+let read: (
+ ~node: fragmentNode<'a>,
+ ~convertFragment: 'fragment => 'fragment,
+ ~fRef: 'b,
+) => 'fragment
+
type paginationLoadMoreOptions = {onComplete?: Js.Nullable.t => unit}
type paginationLoadMoreFn = (~count: int, ~onComplete: option => unit=?) => Disposable.t
diff --git a/packages/rescript-relay/src/RescriptRelay_Internal.res b/packages/rescript-relay/src/RescriptRelay_Internal.res
index d7e7e693..fffdbd92 100644
--- a/packages/rescript-relay/src/RescriptRelay_Internal.res
+++ b/packages/rescript-relay/src/RescriptRelay_Internal.res
@@ -47,3 +47,11 @@ let unwrapUnion: ('a, array) => 'a = %raw(`
`)
let wrapUnion = union => union
+
+external internal_resolverFragmentRefsToFragmentRefs: RescriptRelay.resolverFragmentRefs<
+ 'a,
+> => RescriptRelay.fragmentRefs<'a> = "%identity"
+
+external internal_resolverFragmentRefsToFragmentRefsPlural: RescriptRelay.resolverFragmentRefs<
+ 'a,
+> => array> = "%identity"
diff --git a/packages/rescript-relay/src/RescriptRelay_Internal.resi b/packages/rescript-relay/src/RescriptRelay_Internal.resi
index 964343e1..4496c2f5 100644
--- a/packages/rescript-relay/src/RescriptRelay_Internal.resi
+++ b/packages/rescript-relay/src/RescriptRelay_Internal.resi
@@ -8,3 +8,11 @@ let internal_nullableToOptionalExnHandler: option