react-relay 14.0.0 → 15.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (194) hide show
  1. package/ReactRelayContainerUtils.js.flow +1 -2
  2. package/ReactRelayContext.js +1 -1
  3. package/ReactRelayContext.js.flow +1 -2
  4. package/ReactRelayFragmentContainer.js.flow +6 -4
  5. package/ReactRelayFragmentMockRenderer.js.flow +1 -2
  6. package/ReactRelayLocalQueryRenderer.js.flow +5 -5
  7. package/ReactRelayPaginationContainer.js.flow +21 -14
  8. package/ReactRelayQueryFetcher.js.flow +28 -16
  9. package/ReactRelayQueryRenderer.js.flow +42 -13
  10. package/ReactRelayQueryRendererContext.js.flow +2 -3
  11. package/ReactRelayRefetchContainer.js.flow +9 -9
  12. package/ReactRelayTestMocker.js.flow +3 -3
  13. package/ReactRelayTypes.js.flow +7 -8
  14. package/RelayContext.js.flow +1 -2
  15. package/__flowtests__/ReactRelayFragmentContainer-flowtest.js.flow +4 -5
  16. package/__flowtests__/ReactRelayPaginationContainer-flowtest.js.flow +4 -5
  17. package/__flowtests__/ReactRelayRefetchContainer-flowtest.js.flow +4 -5
  18. package/__flowtests__/RelayModern-flowtest.js.flow +3 -4
  19. package/__flowtests__/RelayModernFlowtest_badref.graphql.js.flow +3 -4
  20. package/__flowtests__/RelayModernFlowtest_notref.graphql.js.flow +3 -4
  21. package/__flowtests__/RelayModernFlowtest_user.graphql.js.flow +3 -4
  22. package/__flowtests__/RelayModernFlowtest_users.graphql.js.flow +3 -4
  23. package/__flowtests__/__generated__/ReactRelayFragmentContainerFlowtest_viewer.graphql.js.flow +3 -1
  24. package/__flowtests__/__generated__/ReactRelayFragmentContainerFlowtest_viewer2.graphql.js.flow +3 -1
  25. package/__flowtests__/__generated__/ReactRelayPaginationContainerFlowtestQuery.graphql.js.flow +4 -2
  26. package/__flowtests__/__generated__/ReactRelayPaginationContainerFlowtest_viewer.graphql.js.flow +3 -1
  27. package/__flowtests__/__generated__/ReactRelayRefetchContainerFlowtestQuery.graphql.js.flow +4 -2
  28. package/__flowtests__/__generated__/ReactRelayRefetchContainerFlowtest_viewer.graphql.js.flow +3 -1
  29. package/__flowtests__/__generated__/RelayModernFlowtest_badref.graphql.js.flow +4 -2
  30. package/__flowtests__/__generated__/RelayModernFlowtest_notref.graphql.js.flow +4 -2
  31. package/__flowtests__/__generated__/RelayModernFlowtest_user.graphql.js.flow +3 -1
  32. package/__flowtests__/__generated__/RelayModernFlowtest_users.graphql.js.flow +3 -1
  33. package/assertFragmentMap.js.flow +1 -2
  34. package/buildReactRelayContainer.js.flow +7 -7
  35. package/getRootVariablesForFragments.js.flow +1 -3
  36. package/hooks.js +1 -1
  37. package/hooks.js.flow +4 -2
  38. package/index.js +1 -1
  39. package/index.js.flow +6 -2
  40. package/isRelayEnvironment.js.flow +1 -2
  41. package/jest-react/enqueueTask.js.flow +1 -1
  42. package/jest-react/index.js.flow +1 -1
  43. package/jest-react/internalAct.js.flow +1 -1
  44. package/legacy.js +1 -1
  45. package/legacy.js.flow +1 -2
  46. package/lib/ReactRelayContainerUtils.js +2 -3
  47. package/lib/ReactRelayContext.js +3 -4
  48. package/lib/ReactRelayFragmentContainer.js +47 -73
  49. package/lib/ReactRelayFragmentMockRenderer.js +2 -4
  50. package/lib/ReactRelayLocalQueryRenderer.js +18 -31
  51. package/lib/ReactRelayPaginationContainer.js +74 -164
  52. package/lib/ReactRelayQueryFetcher.js +49 -76
  53. package/lib/ReactRelayQueryRenderer.js +63 -84
  54. package/lib/ReactRelayQueryRendererContext.js +2 -2
  55. package/lib/ReactRelayRefetchContainer.js +58 -108
  56. package/lib/ReactRelayTestMocker.js +33 -68
  57. package/lib/ReactRelayTypes.js +2 -1
  58. package/lib/RelayContext.js +4 -7
  59. package/lib/assertFragmentMap.js +3 -5
  60. package/lib/buildReactRelayContainer.js +11 -27
  61. package/lib/getRootVariablesForFragments.js +6 -10
  62. package/lib/hooks.js +5 -18
  63. package/lib/index.js +7 -24
  64. package/lib/isRelayEnvironment.js +5 -4
  65. package/lib/jest-react/enqueueTask.js +5 -9
  66. package/lib/jest-react/index.js +0 -1
  67. package/lib/jest-react/internalAct.js +9 -20
  68. package/lib/legacy.js +2 -8
  69. package/lib/multi-actor/ActorChange.js +2 -5
  70. package/lib/multi-actor/index.js +2 -1
  71. package/lib/multi-actor/useRelayActorEnvironment.js +4 -8
  72. package/lib/relay-hooks/EntryPointContainer.react.js +9 -15
  73. package/lib/relay-hooks/EntryPointTypes.flow.js +5 -3
  74. package/lib/relay-hooks/FragmentResource.js +109 -203
  75. package/lib/relay-hooks/HooksImplementation.js +3 -6
  76. package/lib/relay-hooks/InternalLogger.js +2 -3
  77. package/lib/relay-hooks/LRUCache.js +2 -20
  78. package/lib/relay-hooks/LazyLoadEntryPointContainer_DEPRECATED.react.js +33 -54
  79. package/lib/relay-hooks/MatchContainer.js +15 -24
  80. package/lib/relay-hooks/ProfilerContext.js +3 -3
  81. package/lib/relay-hooks/QueryResource.js +31 -101
  82. package/lib/relay-hooks/RelayEnvironmentProvider.js +5 -9
  83. package/lib/relay-hooks/SuspenseResource.js +9 -33
  84. package/lib/relay-hooks/loadEntryPoint.js +19 -31
  85. package/lib/relay-hooks/loadQuery.js +42 -78
  86. package/lib/relay-hooks/preloadQuery_DEPRECATED.js +11 -37
  87. package/lib/relay-hooks/prepareEntryPoint_DEPRECATED.js +9 -15
  88. package/lib/relay-hooks/react-cache/RelayReactCache.js +7 -12
  89. package/lib/relay-hooks/react-cache/getQueryResultOrFetchQuery_REACT_CACHE.js +27 -81
  90. package/lib/relay-hooks/react-cache/readFragmentInternal_REACT_CACHE.js +206 -0
  91. package/lib/relay-hooks/react-cache/useFragmentInternal_REACT_CACHE.js +195 -215
  92. package/lib/relay-hooks/react-cache/useFragment_REACT_CACHE.js +5 -15
  93. package/lib/relay-hooks/react-cache/useLazyLoadQuery_REACT_CACHE.js +17 -24
  94. package/lib/relay-hooks/react-cache/usePaginationFragment_REACT_CACHE.js +149 -0
  95. package/lib/relay-hooks/react-cache/usePreloadedQuery_REACT_CACHE.js +24 -39
  96. package/lib/relay-hooks/react-cache/useRefetchableFragmentInternal_REACT_CACHE.js +325 -0
  97. package/lib/relay-hooks/react-cache/useRefetchableFragment_REACT_CACHE.js +37 -0
  98. package/lib/relay-hooks/useBlockingPaginationFragment.js +73 -93
  99. package/lib/relay-hooks/useClientQuery.js +30 -0
  100. package/lib/relay-hooks/useEntryPointLoader.js +18 -38
  101. package/lib/relay-hooks/useFetchTrackingRef.js +10 -12
  102. package/lib/relay-hooks/useFragment.js +8 -19
  103. package/lib/relay-hooks/useFragmentNode.js +20 -32
  104. package/lib/relay-hooks/useIsMountedRef.js +4 -6
  105. package/lib/relay-hooks/useIsOperationNodeActive.js +8 -20
  106. package/lib/relay-hooks/useIsParentQueryActive.js +3 -6
  107. package/lib/relay-hooks/useLazyLoadQuery.js +7 -24
  108. package/lib/relay-hooks/useLazyLoadQueryNode.js +23 -34
  109. package/lib/relay-hooks/useLoadMoreFunction.js +46 -78
  110. package/lib/relay-hooks/useMemoOperationDescriptor.js +6 -15
  111. package/lib/relay-hooks/useMemoVariables.js +15 -34
  112. package/lib/relay-hooks/useMutation.js +9 -27
  113. package/lib/relay-hooks/usePaginationFragment.js +73 -76
  114. package/lib/relay-hooks/usePreloadedQuery.js +13 -44
  115. package/lib/relay-hooks/useQueryLoader.js +24 -49
  116. package/lib/relay-hooks/useRefetchableFragment.js +19 -17
  117. package/lib/relay-hooks/useRefetchableFragmentNode.js +65 -109
  118. package/lib/relay-hooks/useRelayEnvironment.js +4 -8
  119. package/lib/relay-hooks/useStaticFragmentNodeWarning.js +4 -8
  120. package/lib/relay-hooks/useSubscribeToInvalidationState.js +8 -9
  121. package/lib/relay-hooks/useSubscription.js +5 -10
  122. package/lib/relay-hooks/useUnsafeRef_DEPRECATED.js +29 -0
  123. package/multi-actor/ActorChange.js.flow +1 -1
  124. package/multi-actor/index.js.flow +1 -1
  125. package/multi-actor/useRelayActorEnvironment.js.flow +2 -4
  126. package/package.json +2 -2
  127. package/react-relay-hooks.js +2 -2
  128. package/react-relay-hooks.min.js +2 -2
  129. package/react-relay-legacy.js +2 -2
  130. package/react-relay-legacy.min.js +2 -2
  131. package/react-relay.js +2 -2
  132. package/react-relay.min.js +2 -2
  133. package/relay-hooks/EntryPointContainer.react.js.flow +3 -5
  134. package/relay-hooks/EntryPointTypes.flow.js.flow +37 -37
  135. package/relay-hooks/FragmentResource.js.flow +43 -19
  136. package/relay-hooks/HooksImplementation.js.flow +7 -9
  137. package/relay-hooks/InternalLogger.js.flow +1 -3
  138. package/relay-hooks/LRUCache.js.flow +1 -3
  139. package/relay-hooks/LazyLoadEntryPointContainer_DEPRECATED.react.js.flow +19 -14
  140. package/relay-hooks/MatchContainer.js.flow +6 -8
  141. package/relay-hooks/ProfilerContext.js.flow +1 -3
  142. package/relay-hooks/QueryResource.js.flow +29 -11
  143. package/relay-hooks/RelayEnvironmentProvider.js.flow +4 -6
  144. package/relay-hooks/SuspenseResource.js.flow +1 -3
  145. package/relay-hooks/__flowtests__/EntryPointTypes/EntryPointElementConfig-flowtest.js.flow +6 -4
  146. package/relay-hooks/__flowtests__/EntryPointTypes/NestedEntrypoints-flowtest.js.flow +4 -4
  147. package/relay-hooks/__flowtests__/__generated__/useFragmentFlowtest_user.graphql.js.flow +3 -1
  148. package/relay-hooks/__flowtests__/__generated__/useFragmentFlowtest_users.graphql.js.flow +3 -1
  149. package/relay-hooks/__flowtests__/useBlockingPaginationFragment-flowtest.js.flow +39 -39
  150. package/relay-hooks/__flowtests__/useFragment-flowtest.js.flow +1 -3
  151. package/relay-hooks/__flowtests__/usePaginationFragment-flowtest.js.flow +37 -38
  152. package/relay-hooks/__flowtests__/useRefetchableFragment-flowtest.js.flow +18 -20
  153. package/relay-hooks/__flowtests__/utils.js.flow +21 -12
  154. package/relay-hooks/loadEntryPoint.js.flow +11 -6
  155. package/relay-hooks/loadQuery.js.flow +11 -7
  156. package/relay-hooks/preloadQuery_DEPRECATED.js.flow +9 -12
  157. package/relay-hooks/prepareEntryPoint_DEPRECATED.js.flow +13 -10
  158. package/relay-hooks/react-cache/RelayReactCache.js.flow +1 -3
  159. package/relay-hooks/react-cache/getQueryResultOrFetchQuery_REACT_CACHE.js.flow +26 -20
  160. package/relay-hooks/react-cache/readFragmentInternal_REACT_CACHE.js.flow +297 -0
  161. package/relay-hooks/react-cache/useFragmentInternal_REACT_CACHE.js.flow +136 -96
  162. package/relay-hooks/react-cache/useFragment_REACT_CACHE.js.flow +1 -3
  163. package/relay-hooks/react-cache/useLazyLoadQuery_REACT_CACHE.js.flow +3 -5
  164. package/relay-hooks/react-cache/usePaginationFragment_REACT_CACHE.js.flow +190 -0
  165. package/relay-hooks/react-cache/usePreloadedQuery_REACT_CACHE.js.flow +3 -6
  166. package/relay-hooks/react-cache/useRefetchableFragmentInternal_REACT_CACHE.js.flow +601 -0
  167. package/relay-hooks/react-cache/useRefetchableFragment_REACT_CACHE.js.flow +65 -0
  168. package/relay-hooks/useBlockingPaginationFragment.js.flow +86 -59
  169. package/relay-hooks/useClientQuery.js.flow +39 -0
  170. package/relay-hooks/useEntryPointLoader.js.flow +16 -14
  171. package/relay-hooks/useFetchTrackingRef.js.flow +7 -8
  172. package/relay-hooks/useFragment.js.flow +2 -4
  173. package/relay-hooks/useFragmentNode.js.flow +7 -8
  174. package/relay-hooks/useIsMountedRef.js.flow +2 -4
  175. package/relay-hooks/useIsOperationNodeActive.js.flow +1 -1
  176. package/relay-hooks/useIsParentQueryActive.js.flow +1 -1
  177. package/relay-hooks/useLazyLoadQuery.js.flow +9 -32
  178. package/relay-hooks/useLazyLoadQueryNode.js.flow +4 -6
  179. package/relay-hooks/useLoadMoreFunction.js.flow +20 -17
  180. package/relay-hooks/useMemoOperationDescriptor.js.flow +3 -5
  181. package/relay-hooks/useMemoVariables.js.flow +13 -31
  182. package/relay-hooks/useMutation.js.flow +6 -8
  183. package/relay-hooks/usePaginationFragment.js.flow +75 -43
  184. package/relay-hooks/usePreloadedQuery.js.flow +49 -43
  185. package/relay-hooks/useQueryLoader.js.flow +89 -28
  186. package/relay-hooks/useRefetchableFragment.js.flow +83 -23
  187. package/relay-hooks/useRefetchableFragmentNode.js.flow +26 -22
  188. package/relay-hooks/useRelayEnvironment.js.flow +2 -4
  189. package/relay-hooks/useStaticFragmentNodeWarning.js.flow +3 -5
  190. package/relay-hooks/useSubscribeToInvalidationState.js.flow +2 -4
  191. package/relay-hooks/useSubscription.js.flow +1 -3
  192. package/relay-hooks/useUnsafeRef_DEPRECATED.js.flow +25 -0
  193. package/lib/readContext.js +0 -28
  194. package/readContext.js.flow +0 -31
@@ -4,15 +4,13 @@
4
4
  * This source code is licensed under the MIT license found in the
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  *
7
- * @emails oncall+relay
8
7
  * @flow strict-local
9
8
  * @format
9
+ * @oncall relay
10
10
  */
11
11
 
12
- // flowlint ambiguous-object-type:error
13
-
14
12
  'use strict';
15
-
13
+ import type {OperationType} from '../../relay-runtime/util/RelayRuntimeTypes';
16
14
  import type {
17
15
  EntryPoint,
18
16
  EntryPointComponent,
@@ -42,6 +40,7 @@ function prepareEntryPoint<
42
40
  ): void {
43
41
  // Start loading the code for the entrypoint
44
42
  if (entryPoint.root.getModuleIfRequired() == null) {
43
+ // $FlowFixMe[unused-promise]
45
44
  entryPoint.root.load();
46
45
  }
47
46
  const preloadProps = entryPoint.getPreloadProps(entryPointParams);
@@ -58,7 +57,7 @@ function prepareEntryPoint<
58
57
  environmentProviderOptions,
59
58
  );
60
59
 
61
- preloadedQueries[queryPropName] = preloadQuery(
60
+ preloadedQueries[queryPropName] = preloadQuery<OperationType, mixed>(
62
61
  environment,
63
62
  parameters,
64
63
  variables,
@@ -77,11 +76,15 @@ function prepareEntryPoint<
77
76
  }
78
77
  const {entryPoint: nestedEntryPoint, entryPointParams: nestedParams} =
79
78
  entryPointDescription;
80
- preloadedEntryPoints[entryPointPropName] = prepareEntryPoint(
81
- environmentProvider,
82
- nestedEntryPoint,
83
- nestedParams,
84
- );
79
+ preloadedEntryPoints[entryPointPropName] = prepareEntryPoint<
80
+ TEntryPointParams,
81
+ TPreloadedQueries,
82
+ TPreloadedEntryPoints,
83
+ TRuntimeProps,
84
+ TExtraProps,
85
+ TEntryPointComponent,
86
+ TEntryPoint,
87
+ >(environmentProvider, nestedEntryPoint, nestedParams);
85
88
  });
86
89
  }
87
90
  }
@@ -5,12 +5,10 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  *
7
7
  * @flow strict-local
8
- * @emails oncall+relay
9
8
  * @format
9
+ * @oncall relay
10
10
  */
11
11
 
12
- // flowlint ambiguous-object-type:error
13
-
14
12
  'use strict';
15
13
 
16
14
  const invariant = require('invariant');
@@ -5,12 +5,10 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  *
7
7
  * @flow strict-local
8
- * @emails oncall+relay
9
8
  * @format
9
+ * @oncall relay
10
10
  */
11
11
 
12
- // flowlint ambiguous-object-type:error
13
-
14
12
  'use strict';
15
13
 
16
14
  import type {
@@ -27,17 +25,17 @@ const SuspenseResource = require('../SuspenseResource');
27
25
  const {getCacheForType, getCacheSignal} = require('./RelayReactCache');
28
26
  const invariant = require('invariant');
29
27
  const {
30
- RelayFeatureFlags,
31
28
  __internal: {fetchQuery: fetchQueryInternal},
29
+ RelayFeatureFlags,
32
30
  } = require('relay-runtime');
33
31
  const warning = require('warning');
34
32
 
35
33
  type QueryCacheCommitable = () => () => void;
36
34
 
37
- type QueryResult = {|
35
+ type QueryResult = {
38
36
  fragmentNode: ReaderFragment,
39
37
  fragmentRef: mixed,
40
- |};
38
+ };
41
39
 
42
40
  // Note that the status of a cache entry will be 'resolved' when partial
43
41
  // rendering is allowed, even if a fetch is ongoing. The pending status
@@ -49,34 +47,42 @@ type QueryResult = {|
49
47
  // before any components have mounted. It is unused when Legacy Timeouts
50
48
  // mode is off.
51
49
  type QueryCacheEntryStatus =
52
- | {|
50
+ | {
53
51
  status: 'resolved',
54
52
  result: QueryResult,
55
- |}
56
- | {|
53
+ }
54
+ | {
57
55
  status: 'pending',
58
56
  promise: Promise<void>,
59
- |}
60
- | {|
57
+ }
58
+ | {
61
59
  status: 'rejected',
62
60
  error: Error,
63
- |};
61
+ };
64
62
 
65
- type QueryCacheEntry = {|
63
+ type QueryCacheEntry = {
66
64
  ...QueryCacheEntryStatus,
67
65
  onCommit: QueryCacheCommitable,
68
66
  suspenseResource: SuspenseResource | null,
69
- |};
67
+ };
70
68
 
71
69
  const DEFAULT_FETCH_POLICY = 'store-or-network';
72
70
 
71
+ const WEAKMAP_SUPPORTED = typeof WeakMap === 'function';
72
+
73
+ interface IMap<K, V> {
74
+ delete(key: K): boolean;
75
+ get(key: K): V | void;
76
+ set(key: K, value: V): IMap<K, V>;
77
+ }
78
+
73
79
  type QueryCacheKey = string;
74
80
 
75
81
  class QueryCache {
76
- _map: Map<IEnvironment, Map<QueryCacheKey, QueryCacheEntry>>;
82
+ _map: IMap<IEnvironment, Map<QueryCacheKey, QueryCacheEntry>>;
77
83
 
78
84
  constructor() {
79
- this._map = new Map();
85
+ this._map = WEAKMAP_SUPPORTED ? new WeakMap() : new Map();
80
86
  }
81
87
 
82
88
  get(environment: IEnvironment, key: QueryCacheKey): QueryCacheEntry | void {
@@ -121,7 +127,7 @@ const noopOnCommit = () => {
121
127
  return () => undefined;
122
128
  };
123
129
 
124
- const noopPromise = new Promise(() => {});
130
+ const noopPromise = new Promise<void>(() => {});
125
131
 
126
132
  function getQueryCacheKey(
127
133
  operation: OperationDescriptor,
@@ -160,12 +166,12 @@ function makeInitialCacheEntry() {
160
166
  function getQueryResultOrFetchQuery_REACT_CACHE(
161
167
  environment: IEnvironment,
162
168
  queryOperationDescriptor: OperationDescriptor,
163
- options?: {|
169
+ options?: {
164
170
  fetchPolicy?: FetchPolicy,
165
171
  renderPolicy?: RenderPolicy,
166
172
  fetchKey?: ?string | ?number,
167
173
  fetchObservable?: Observable<GraphQLResponse>,
168
- |},
174
+ },
169
175
  ): [QueryResult, QueryCacheCommitable] {
170
176
  const fetchPolicy = options?.fetchPolicy ?? DEFAULT_FETCH_POLICY;
171
177
  const renderPolicy =
@@ -369,7 +375,7 @@ function executeOperationAndKeepUpToDate(
369
375
  customFetchObservable?: Observable<GraphQLResponse>,
370
376
  ) {
371
377
  let resolvePromise;
372
- const promise = new Promise(r => {
378
+ const promise = new Promise<void>(r => {
373
379
  resolvePromise = r;
374
380
  });
375
381
  // $FlowExpectedError[prop-missing] Expando to annotate Promises.
@@ -0,0 +1,297 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @flow strict-local
8
+ * @format
9
+ * @oncall relay
10
+ */
11
+
12
+ 'use strict';
13
+
14
+ import type {QueryResult} from '../QueryResource';
15
+ import type {
16
+ CacheConfig,
17
+ FetchPolicy,
18
+ IEnvironment,
19
+ ReaderFragment,
20
+ ReaderSelector,
21
+ SelectorData,
22
+ Snapshot,
23
+ } from 'relay-runtime';
24
+ import type {MissingClientEdgeRequestInfo} from 'relay-runtime/store/RelayStoreTypes';
25
+
26
+ const {getQueryResourceForEnvironment} = require('../QueryResource');
27
+ const invariant = require('invariant');
28
+ const {
29
+ __internal: {fetchQuery: fetchQueryInternal},
30
+ createOperationDescriptor,
31
+ getPendingOperationsForFragment,
32
+ getSelector,
33
+ getVariablesFromFragment,
34
+ handlePotentialSnapshotErrors,
35
+ } = require('relay-runtime');
36
+ const warning = require('warning');
37
+
38
+ type FragmentQueryOptions = {
39
+ fetchPolicy?: FetchPolicy,
40
+ networkCacheConfig?: ?CacheConfig,
41
+ };
42
+
43
+ type FragmentState = $ReadOnly<
44
+ | {kind: 'bailout'}
45
+ | {kind: 'singular', snapshot: Snapshot, epoch: number}
46
+ | {kind: 'plural', snapshots: $ReadOnlyArray<Snapshot>, epoch: number},
47
+ >;
48
+
49
+ function isMissingData(state: FragmentState): boolean {
50
+ if (state.kind === 'bailout') {
51
+ return false;
52
+ } else if (state.kind === 'singular') {
53
+ return state.snapshot.isMissingData;
54
+ } else {
55
+ return state.snapshots.some(s => s.isMissingData);
56
+ }
57
+ }
58
+
59
+ function getMissingClientEdges(
60
+ state: FragmentState,
61
+ ): $ReadOnlyArray<MissingClientEdgeRequestInfo> | null {
62
+ if (state.kind === 'bailout') {
63
+ return null;
64
+ } else if (state.kind === 'singular') {
65
+ return state.snapshot.missingClientEdges ?? null;
66
+ } else {
67
+ let edges: null | Array<MissingClientEdgeRequestInfo> = null;
68
+ for (const snapshot of state.snapshots) {
69
+ if (snapshot.missingClientEdges) {
70
+ edges = edges ?? [];
71
+ for (const edge of snapshot.missingClientEdges) {
72
+ edges.push(edge);
73
+ }
74
+ }
75
+ }
76
+ return edges;
77
+ }
78
+ }
79
+
80
+ function handlePotentialSnapshotErrorsForState(
81
+ environment: IEnvironment,
82
+ state: FragmentState,
83
+ ): void {
84
+ if (state.kind === 'singular') {
85
+ handlePotentialSnapshotErrors(
86
+ environment,
87
+ state.snapshot.missingRequiredFields,
88
+ state.snapshot.relayResolverErrors,
89
+ );
90
+ } else if (state.kind === 'plural') {
91
+ for (const snapshot of state.snapshots) {
92
+ handlePotentialSnapshotErrors(
93
+ environment,
94
+ snapshot.missingRequiredFields,
95
+ snapshot.relayResolverErrors,
96
+ );
97
+ }
98
+ }
99
+ }
100
+
101
+ function handleMissingClientEdge(
102
+ environment: IEnvironment,
103
+ parentFragmentNode: ReaderFragment,
104
+ parentFragmentRef: mixed,
105
+ missingClientEdgeRequestInfo: MissingClientEdgeRequestInfo,
106
+ queryOptions?: FragmentQueryOptions,
107
+ ): QueryResult {
108
+ const originalVariables = getVariablesFromFragment(
109
+ parentFragmentNode,
110
+ parentFragmentRef,
111
+ );
112
+ const variables = {
113
+ ...originalVariables,
114
+ id: missingClientEdgeRequestInfo.clientEdgeDestinationID, // TODO should be a reserved name
115
+ };
116
+ const queryOperationDescriptor = createOperationDescriptor(
117
+ missingClientEdgeRequestInfo.request,
118
+ variables,
119
+ queryOptions?.networkCacheConfig,
120
+ );
121
+ // This may suspend. We don't need to do anything with the results; all we're
122
+ // doing here is started the query if needed and retaining and releasing it
123
+ // according to the component mount/suspense cycle; QueryResource
124
+ // already handles this by itself.
125
+ const QueryResource = getQueryResourceForEnvironment(environment);
126
+ return QueryResource.prepare(
127
+ queryOperationDescriptor,
128
+ fetchQueryInternal(environment, queryOperationDescriptor),
129
+ queryOptions?.fetchPolicy,
130
+ );
131
+ }
132
+
133
+ function getFragmentState(
134
+ environment: IEnvironment,
135
+ fragmentSelector: ?ReaderSelector,
136
+ ): FragmentState {
137
+ if (fragmentSelector == null) {
138
+ return {kind: 'bailout'};
139
+ } else if (fragmentSelector.kind === 'PluralReaderSelector') {
140
+ if (fragmentSelector.selectors.length === 0) {
141
+ return {kind: 'bailout'};
142
+ } else {
143
+ return {
144
+ kind: 'plural',
145
+ snapshots: fragmentSelector.selectors.map(s => environment.lookup(s)),
146
+ epoch: environment.getStore().getEpoch(),
147
+ };
148
+ }
149
+ } else {
150
+ return {
151
+ kind: 'singular',
152
+ snapshot: environment.lookup(fragmentSelector),
153
+ epoch: environment.getStore().getEpoch(),
154
+ };
155
+ }
156
+ }
157
+
158
+ // fragmentNode cannot change during the lifetime of the component, though fragmentRef may change.
159
+ function readFragmentInternal_REACT_CACHE(
160
+ environment: IEnvironment,
161
+ fragmentNode: ReaderFragment,
162
+ fragmentRef: mixed,
163
+ hookDisplayName: string,
164
+ queryOptions?: FragmentQueryOptions,
165
+ fragmentKey?: string,
166
+ ): {
167
+ +data: ?SelectorData | Array<?SelectorData>,
168
+ +clientEdgeQueries: ?Array<QueryResult>,
169
+ } {
170
+ const fragmentSelector = getSelector(fragmentNode, fragmentRef);
171
+ const isPlural = fragmentNode?.metadata?.plural === true;
172
+
173
+ if (isPlural) {
174
+ invariant(
175
+ fragmentRef == null || Array.isArray(fragmentRef),
176
+ 'Relay: Expected fragment pointer%s for fragment `%s` to be ' +
177
+ 'an array, instead got `%s`. Remove `@relay(plural: true)` ' +
178
+ 'from fragment `%s` to allow the prop to be an object.',
179
+ fragmentKey != null ? ` for key \`${fragmentKey}\`` : '',
180
+ fragmentNode.name,
181
+ typeof fragmentRef,
182
+ fragmentNode.name,
183
+ );
184
+ } else {
185
+ invariant(
186
+ !Array.isArray(fragmentRef),
187
+ 'Relay: Expected fragment pointer%s for fragment `%s` not to be ' +
188
+ 'an array, instead got `%s`. Add `@relay(plural: true)` ' +
189
+ 'to fragment `%s` to allow the prop to be an array.',
190
+ fragmentKey != null ? ` for key \`${fragmentKey}\`` : '',
191
+ fragmentNode.name,
192
+ typeof fragmentRef,
193
+ fragmentNode.name,
194
+ );
195
+ }
196
+ invariant(
197
+ fragmentRef == null ||
198
+ (isPlural && Array.isArray(fragmentRef) && fragmentRef.length === 0) ||
199
+ fragmentSelector != null,
200
+ 'Relay: Expected to receive an object where `...%s` was spread, ' +
201
+ 'but the fragment reference was not found`. This is most ' +
202
+ 'likely the result of:\n' +
203
+ "- Forgetting to spread `%s` in `%s`'s parent's fragment.\n" +
204
+ '- Conditionally fetching `%s` but unconditionally passing %s prop ' +
205
+ 'to `%s`. If the parent fragment only fetches the fragment conditionally ' +
206
+ '- with e.g. `@include`, `@skip`, or inside a `... on SomeType { }` ' +
207
+ 'spread - then the fragment reference will not exist. ' +
208
+ 'In this case, pass `null` if the conditions for evaluating the ' +
209
+ 'fragment are not met (e.g. if the `@include(if)` value is false.)',
210
+ fragmentNode.name,
211
+ fragmentNode.name,
212
+ hookDisplayName,
213
+ fragmentNode.name,
214
+ fragmentKey == null ? 'a fragment reference' : `the \`${fragmentKey}\``,
215
+ hookDisplayName,
216
+ );
217
+
218
+ const state = getFragmentState(environment, fragmentSelector);
219
+
220
+ // Handle the queries for any missing client edges; this may suspend.
221
+ // FIXME handle client edges in parallel.
222
+ let clientEdgeQueries = null;
223
+ if (fragmentNode.metadata?.hasClientEdges === true) {
224
+ const missingClientEdges = getMissingClientEdges(state);
225
+ if (missingClientEdges?.length) {
226
+ clientEdgeQueries = ([]: Array<QueryResult>);
227
+ for (const edge of missingClientEdges) {
228
+ clientEdgeQueries.push(
229
+ handleMissingClientEdge(
230
+ environment,
231
+ fragmentNode,
232
+ fragmentRef,
233
+ edge,
234
+ queryOptions,
235
+ ),
236
+ );
237
+ }
238
+ }
239
+ }
240
+
241
+ if (isMissingData(state)) {
242
+ // Suspend if an active operation bears on this fragment, either the
243
+ // fragment's owner or some other mutation etc. that could affect it:
244
+ invariant(fragmentSelector != null, 'refinement, see invariants above');
245
+ const fragmentOwner =
246
+ fragmentSelector.kind === 'PluralReaderSelector'
247
+ ? fragmentSelector.selectors[0].owner
248
+ : fragmentSelector.owner;
249
+ const pendingOperationsResult = getPendingOperationsForFragment(
250
+ environment,
251
+ fragmentNode,
252
+ fragmentOwner,
253
+ );
254
+ if (pendingOperationsResult) {
255
+ throw pendingOperationsResult.promise;
256
+ }
257
+ // Report required fields only if we're not suspending, since that means
258
+ // they're missing even though we are out of options for possibly fetching them:
259
+ handlePotentialSnapshotErrorsForState(environment, state);
260
+ }
261
+
262
+ let data: ?SelectorData | Array<?SelectorData>;
263
+ if (state.kind === 'bailout') {
264
+ data = isPlural ? [] : null;
265
+ } else if (state.kind === 'singular') {
266
+ data = state.snapshot.data;
267
+ } else {
268
+ data = state.snapshots.map(s => s.data);
269
+ }
270
+
271
+ if (__DEV__) {
272
+ if (
273
+ fragmentRef != null &&
274
+ (data === undefined ||
275
+ (Array.isArray(data) &&
276
+ data.length > 0 &&
277
+ data.every(d => d === undefined)))
278
+ ) {
279
+ warning(
280
+ false,
281
+ 'Relay: Expected to have been able to read non-null data for ' +
282
+ 'fragment `%s` declared in ' +
283
+ '`%s`, since fragment reference was non-null. ' +
284
+ "Make sure that that `%s`'s parent isn't " +
285
+ 'holding on to and/or passing a fragment reference for data that ' +
286
+ 'has been deleted.',
287
+ fragmentNode.name,
288
+ hookDisplayName,
289
+ hookDisplayName,
290
+ );
291
+ }
292
+ }
293
+
294
+ return {data, clientEdgeQueries};
295
+ }
296
+
297
+ module.exports = readFragmentInternal_REACT_CACHE;