relay-compiler 9.0.0 → 9.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (197) hide show
  1. package/bin/RelayCompilerBin.js.flow +169 -0
  2. package/bin/RelayCompilerMain.js.flow +508 -0
  3. package/bin/__fixtures__/plugin-module.js.flow +17 -0
  4. package/bin/relay-compiler +2002 -1733
  5. package/codegen/CodegenDirectory.js.flow +375 -0
  6. package/codegen/CodegenRunner.js.flow +431 -0
  7. package/codegen/CodegenTypes.js.flow +28 -0
  8. package/codegen/CodegenWatcher.js.flow +254 -0
  9. package/codegen/NormalizationCodeGenerator.js.flow +499 -0
  10. package/codegen/ReaderCodeGenerator.js.flow +453 -0
  11. package/codegen/RelayCodeGenerator.js.flow +76 -0
  12. package/codegen/RelayFileWriter.js.flow +366 -0
  13. package/codegen/SourceControl.js.flow +58 -0
  14. package/codegen/compileRelayArtifacts.js.flow +182 -0
  15. package/codegen/createPrintRequireModuleDependency.js.flow +21 -0
  16. package/codegen/writeRelayGeneratedFile.js.flow +194 -0
  17. package/core/ASTCache.js.flow +73 -0
  18. package/core/ASTConvert.js.flow +233 -0
  19. package/core/CompilerContext.js.flow +190 -0
  20. package/core/CompilerError.js.flow +250 -0
  21. package/core/DotGraphQLParser.js.flow +39 -0
  22. package/core/GraphQLCompilerProfiler.js.flow +341 -0
  23. package/core/GraphQLDerivedFromMetadata.js.flow +48 -0
  24. package/core/GraphQLWatchmanClient.js.flow +111 -0
  25. package/core/IR.js.flow +329 -0
  26. package/core/IRPrinter.js.flow +488 -0
  27. package/core/IRTransformer.js.flow +377 -0
  28. package/core/IRValidator.js.flow +260 -0
  29. package/core/IRVisitor.js.flow +150 -0
  30. package/core/JSModuleParser.js.flow +24 -0
  31. package/core/RelayCompilerScope.js.flow +199 -0
  32. package/core/RelayFindGraphQLTags.js.flow +119 -0
  33. package/core/RelayGraphQLEnumsGenerator.js.flow +55 -0
  34. package/core/RelayIRTransforms.js.flow +130 -0
  35. package/core/RelayParser.js.flow +1759 -0
  36. package/core/RelaySourceModuleParser.js.flow +135 -0
  37. package/core/Schema.js.flow +1985 -0
  38. package/core/SchemaUtils.js.flow +109 -0
  39. package/core/filterContextForNode.js.flow +50 -0
  40. package/core/getFieldDefinition.js.flow +156 -0
  41. package/core/getIdentifierForArgumentValue.js.flow +49 -0
  42. package/core/getIdentifierForSelection.js.flow +69 -0
  43. package/core/getLiteralArgumentValues.js.flow +32 -0
  44. package/core/getNormalizationOperationName.js.flow +19 -0
  45. package/core/inferRootArgumentDefinitions.js.flow +323 -0
  46. package/index.js +1 -1
  47. package/index.js.flow +202 -0
  48. package/language/RelayLanguagePluginInterface.js.flow +283 -0
  49. package/language/javascript/FindGraphQLTags.js.flow +233 -0
  50. package/language/javascript/RelayFlowBabelFactories.js.flow +180 -0
  51. package/language/javascript/RelayFlowGenerator.js.flow +1040 -0
  52. package/language/javascript/RelayFlowTypeTransformers.js.flow +184 -0
  53. package/language/javascript/RelayLanguagePluginJavaScript.js.flow +34 -0
  54. package/language/javascript/formatGeneratedModule.js.flow +65 -0
  55. package/lib/bin/RelayCompilerBin.js +10 -0
  56. package/lib/bin/RelayCompilerMain.js +113 -119
  57. package/lib/codegen/CodegenDirectory.js +2 -6
  58. package/lib/codegen/CodegenRunner.js +34 -75
  59. package/lib/codegen/CodegenWatcher.js +13 -21
  60. package/lib/codegen/NormalizationCodeGenerator.js +43 -40
  61. package/lib/codegen/ReaderCodeGenerator.js +43 -35
  62. package/lib/codegen/RelayCodeGenerator.js +7 -5
  63. package/lib/codegen/RelayFileWriter.js +15 -36
  64. package/lib/codegen/compileRelayArtifacts.js +16 -30
  65. package/lib/codegen/writeRelayGeneratedFile.js +56 -98
  66. package/lib/core/ASTCache.js +1 -3
  67. package/lib/core/CompilerContext.js +1 -3
  68. package/lib/core/CompilerError.js +27 -54
  69. package/lib/core/GraphQLCompilerProfiler.js +8 -12
  70. package/lib/core/GraphQLWatchmanClient.js +4 -12
  71. package/lib/core/IRPrinter.js +5 -5
  72. package/lib/core/IRTransformer.js +8 -6
  73. package/lib/core/IRValidator.js +1 -3
  74. package/lib/core/RelayCompilerScope.js +4 -4
  75. package/lib/core/RelayGraphQLEnumsGenerator.js +12 -15
  76. package/lib/core/RelayIRTransforms.js +9 -5
  77. package/lib/core/RelayParser.js +44 -56
  78. package/lib/core/RelaySourceModuleParser.js +1 -3
  79. package/lib/core/Schema.js +78 -65
  80. package/lib/core/getFieldDefinition.js +12 -15
  81. package/lib/core/getIdentifierForSelection.js +1 -1
  82. package/lib/core/inferRootArgumentDefinitions.js +27 -36
  83. package/lib/language/javascript/RelayFlowGenerator.js +49 -78
  84. package/lib/language/javascript/RelayFlowTypeTransformers.js +1 -3
  85. package/lib/reporters/ConsoleReporter.js +1 -3
  86. package/lib/reporters/MultiReporter.js +1 -3
  87. package/lib/runner/Artifacts.js +69 -170
  88. package/lib/runner/BufferedFilesystem.js +32 -66
  89. package/lib/runner/GraphQLASTNodeGroup.js +54 -120
  90. package/lib/runner/GraphQLNodeMap.js +14 -19
  91. package/lib/runner/Sources.js +49 -78
  92. package/lib/runner/StrictMap.js +20 -36
  93. package/lib/runner/getChangedNodeNames.js +30 -62
  94. package/lib/transforms/ApplyFragmentArgumentTransform.js +33 -28
  95. package/lib/transforms/ClientExtensionsTransform.js +7 -12
  96. package/lib/transforms/ConnectionTransform.js +26 -24
  97. package/lib/transforms/DeferStreamTransform.js +20 -16
  98. package/lib/transforms/DisallowTypenameOnRoot.js +55 -0
  99. package/lib/transforms/FieldHandleTransform.js +6 -2
  100. package/lib/transforms/FlattenTransform.js +19 -14
  101. package/lib/transforms/GenerateIDFieldTransform.js +7 -3
  102. package/lib/transforms/GenerateTypeNameTransform.js +6 -2
  103. package/lib/transforms/InlineDataFragmentTransform.js +7 -3
  104. package/lib/transforms/MaskTransform.js +17 -17
  105. package/lib/transforms/MatchTransform.js +110 -32
  106. package/lib/transforms/RefetchableFragmentTransform.js +21 -17
  107. package/lib/transforms/RelayDirectiveTransform.js +11 -3
  108. package/lib/transforms/SkipClientExtensionsTransform.js +8 -0
  109. package/lib/transforms/SkipHandleFieldTransform.js +6 -2
  110. package/lib/transforms/SkipRedundantNodesTransform.js +6 -2
  111. package/lib/transforms/SkipSplitOperationTransform.js +32 -0
  112. package/lib/transforms/SkipUnreachableNodeTransform.js +9 -2
  113. package/lib/transforms/SkipUnusedVariablesTransform.js +18 -17
  114. package/lib/transforms/SplitModuleImportTransform.js +2 -2
  115. package/lib/transforms/TestOperationTransform.js +7 -3
  116. package/lib/transforms/ValidateGlobalVariablesTransform.js +18 -30
  117. package/lib/transforms/ValidateRequiredArgumentsTransform.js +12 -15
  118. package/lib/transforms/ValidateServerOnlyDirectivesTransform.js +16 -30
  119. package/lib/transforms/ValidateUnusedVariablesTransform.js +18 -30
  120. package/lib/transforms/query-generators/FetchableQueryGenerator.js +161 -0
  121. package/lib/transforms/query-generators/NodeQueryGenerator.js +7 -2
  122. package/lib/transforms/query-generators/QueryQueryGenerator.js +1 -0
  123. package/lib/transforms/query-generators/ViewerQueryGenerator.js +1 -0
  124. package/lib/transforms/query-generators/index.js +23 -6
  125. package/lib/transforms/query-generators/utils.js +12 -15
  126. package/lib/util/RelayCompilerCache.js +1 -3
  127. package/lib/util/dedupeJSONStringify.js +15 -12
  128. package/lib/util/getModuleName.js +1 -1
  129. package/package.json +2 -2
  130. package/relay-compiler.js +4 -4
  131. package/relay-compiler.min.js +4 -4
  132. package/reporters/ConsoleReporter.js.flow +81 -0
  133. package/reporters/MultiReporter.js.flow +43 -0
  134. package/reporters/Reporter.js.flow +19 -0
  135. package/runner/Artifacts.js.flow +219 -0
  136. package/runner/BufferedFilesystem.js.flow +194 -0
  137. package/runner/GraphQLASTNodeGroup.js.flow +176 -0
  138. package/runner/GraphQLASTUtils.js.flow +26 -0
  139. package/runner/GraphQLNodeMap.js.flow +55 -0
  140. package/runner/Sources.js.flow +218 -0
  141. package/runner/StrictMap.js.flow +96 -0
  142. package/runner/compileArtifacts.js.flow +76 -0
  143. package/runner/extractAST.js.flow +100 -0
  144. package/runner/getChangedNodeNames.js.flow +48 -0
  145. package/runner/getSchemaInstance.js.flow +36 -0
  146. package/runner/types.js.flow +37 -0
  147. package/transforms/ApplyFragmentArgumentTransform.js.flow +474 -0
  148. package/transforms/ClientExtensionsTransform.js.flow +220 -0
  149. package/transforms/ConnectionTransform.js.flow +869 -0
  150. package/transforms/DeferStreamTransform.js.flow +258 -0
  151. package/transforms/DisallowIdAsAlias.js.flow +47 -0
  152. package/transforms/DisallowTypenameOnRoot.js.flow +45 -0
  153. package/transforms/FieldHandleTransform.js.flow +80 -0
  154. package/transforms/FilterDirectivesTransform.js.flow +45 -0
  155. package/transforms/FlattenTransform.js.flow +456 -0
  156. package/transforms/GenerateIDFieldTransform.js.flow +134 -0
  157. package/transforms/GenerateTypeNameTransform.js.flow +81 -0
  158. package/transforms/InlineDataFragmentTransform.js.flow +124 -0
  159. package/transforms/InlineFragmentsTransform.js.flow +71 -0
  160. package/transforms/MaskTransform.js.flow +126 -0
  161. package/transforms/MatchTransform.js.flow +583 -0
  162. package/transforms/RefetchableFragmentTransform.js.flow +272 -0
  163. package/transforms/RelayDirectiveTransform.js.flow +99 -0
  164. package/transforms/SkipClientExtensionsTransform.js.flow +54 -0
  165. package/transforms/SkipHandleFieldTransform.js.flow +44 -0
  166. package/transforms/SkipRedundantNodesTransform.js.flow +253 -0
  167. package/transforms/SkipSplitOperationTransform.js.flow +37 -0
  168. package/transforms/SkipUnreachableNodeTransform.js.flow +149 -0
  169. package/transforms/SkipUnusedVariablesTransform.js.flow +59 -0
  170. package/transforms/SplitModuleImportTransform.js.flow +98 -0
  171. package/transforms/TestOperationTransform.js.flow +138 -0
  172. package/transforms/TransformUtils.js.flow +26 -0
  173. package/transforms/ValidateGlobalVariablesTransform.js.flow +81 -0
  174. package/transforms/ValidateRequiredArgumentsTransform.js.flow +127 -0
  175. package/transforms/ValidateServerOnlyDirectivesTransform.js.flow +112 -0
  176. package/transforms/ValidateUnusedVariablesTransform.js.flow +89 -0
  177. package/transforms/query-generators/FetchableQueryGenerator.js.flow +190 -0
  178. package/transforms/query-generators/NodeQueryGenerator.js.flow +206 -0
  179. package/transforms/query-generators/QueryQueryGenerator.js.flow +57 -0
  180. package/transforms/query-generators/ViewerQueryGenerator.js.flow +97 -0
  181. package/transforms/query-generators/index.js.flow +90 -0
  182. package/transforms/query-generators/utils.js.flow +72 -0
  183. package/util/CodeMarker.js.flow +79 -0
  184. package/util/DefaultHandleKey.js.flow +17 -0
  185. package/util/RelayCompilerCache.js.flow +88 -0
  186. package/util/Rollout.js.flow +39 -0
  187. package/util/TimeReporter.js.flow +79 -0
  188. package/util/areEqualOSS.js.flow +123 -0
  189. package/util/dedupeJSONStringify.js.flow +152 -0
  190. package/util/getDefinitionNodeHash.js.flow +25 -0
  191. package/util/getModuleName.js.flow +39 -0
  192. package/util/joinArgumentDefinitions.js.flow +99 -0
  193. package/util/md5.js.flow +22 -0
  194. package/util/murmurHash.js.flow +94 -0
  195. package/util/nullthrowsOSS.js.flow +25 -0
  196. package/util/orList.js.flow +37 -0
  197. package/util/partitionArray.js.flow +37 -0
@@ -0,0 +1,456 @@
1
+ /**
2
+ * Copyright (c) Facebook, Inc. and its 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
+ * @format
8
+ * @flow
9
+ */
10
+
11
+ // flowlint ambiguous-object-type:error
12
+
13
+ 'use strict';
14
+
15
+ const IRTransformer = require('../core/IRTransformer');
16
+
17
+ const areEqual = require('../util/areEqualOSS');
18
+ const getIdentifierForSelection = require('../core/getIdentifierForSelection');
19
+
20
+ const {createCompilerError, createUserError} = require('../core/CompilerError');
21
+
22
+ import type CompilerContext from '../core/CompilerContext';
23
+ import type {
24
+ Argument,
25
+ Field,
26
+ Handle,
27
+ InlineFragment,
28
+ LinkedField,
29
+ Node,
30
+ ScalarField,
31
+ Selection,
32
+ } from '../core/IR';
33
+ import type {Schema, TypeID} from '../core/Schema';
34
+
35
+ export type FlattenOptions = {|+isForCodegen?: boolean|};
36
+
37
+ type State = {|
38
+ +isForCodegen: boolean,
39
+ parentType: ?TypeID,
40
+ |};
41
+
42
+ /**
43
+ * Transform that flattens inline fragments, fragment spreads, and conditionals.
44
+ *
45
+ * Inline fragments are inlined (replaced with their selections) when:
46
+ * - The fragment type matches the type of its parent, and its `isForCodegen`,
47
+ * or if it's for printing, there is no directive on the inline fragment.
48
+ * - The fragment has an abstract type and the `isForCodegen` option has
49
+ * been set.
50
+ */
51
+ function flattenTransformImpl(
52
+ context: CompilerContext,
53
+ options?: FlattenOptions,
54
+ ): CompilerContext {
55
+ const state = {
56
+ isForCodegen: !!(options && options.isForCodegen),
57
+ parentType: null,
58
+ };
59
+ const visitorFn = memoizedFlattenSelection(new Map());
60
+ return IRTransformer.transform(
61
+ context,
62
+ {
63
+ Condition: visitorFn,
64
+ Defer: visitorFn,
65
+ Fragment: visitorFn,
66
+ InlineFragment: visitorFn,
67
+ InlineDataFragmentSpread: visitorFn,
68
+ LinkedField: visitorFn,
69
+ Root: visitorFn,
70
+ SplitOperation: visitorFn,
71
+ },
72
+ () => state,
73
+ );
74
+ }
75
+
76
+ function memoizedFlattenSelection(cache) {
77
+ return function flattenSelectionsFn<T: Node>(node: T, state: State): T {
78
+ const context: CompilerContext = this.getContext();
79
+ let nodeCache = cache.get(node);
80
+ if (nodeCache == null) {
81
+ nodeCache = new Map();
82
+ cache.set(node, nodeCache);
83
+ }
84
+ // Determine the current type.
85
+ const parentType = state.parentType;
86
+ const result = nodeCache.get(parentType);
87
+ if (result != null) {
88
+ return result;
89
+ }
90
+
91
+ const type =
92
+ node.kind === 'LinkedField' ||
93
+ node.kind === 'Fragment' ||
94
+ node.kind === 'Root' ||
95
+ node.kind === 'SplitOperation'
96
+ ? node.type
97
+ : node.kind === 'InlineFragment'
98
+ ? node.typeCondition
99
+ : parentType;
100
+ if (type == null) {
101
+ throw createCompilerError('FlattenTransform: Expected a parent type.', [
102
+ node.loc,
103
+ ]);
104
+ }
105
+
106
+ // Flatten the selections in this node, creating a new node with flattened
107
+ // selections if possible, then deeply traverse the flattened node, while
108
+ // keeping track of the parent type.
109
+ const nextSelections = new Map();
110
+ const hasFlattened = flattenSelectionsInto(
111
+ context.getSchema(),
112
+ nextSelections,
113
+ node,
114
+ state,
115
+ type,
116
+ );
117
+ const flattenedNode = hasFlattened
118
+ ? {...node, selections: Array.from(nextSelections.values())}
119
+ : node;
120
+ state.parentType = type;
121
+ const deeplyFlattenedNode = this.traverse(flattenedNode, state);
122
+ state.parentType = parentType;
123
+ nodeCache.set(parentType, deeplyFlattenedNode);
124
+ return deeplyFlattenedNode;
125
+ };
126
+ }
127
+
128
+ /**
129
+ * @private
130
+ */
131
+ function flattenSelectionsInto(
132
+ schema: Schema,
133
+ flattenedSelections: Map<string, Selection>,
134
+ node: Node,
135
+ state: State,
136
+ type: TypeID,
137
+ ): boolean {
138
+ let hasFlattened = false;
139
+ node.selections.forEach(selection => {
140
+ if (
141
+ selection.kind === 'InlineFragment' &&
142
+ shouldFlattenInlineFragment(schema, selection, state, type)
143
+ ) {
144
+ hasFlattened = true;
145
+ flattenSelectionsInto(
146
+ schema,
147
+ flattenedSelections,
148
+ selection,
149
+ state,
150
+ type,
151
+ );
152
+ return;
153
+ }
154
+ const nodeIdentifier = getIdentifierForSelection(schema, selection);
155
+ const flattenedSelection = flattenedSelections.get(nodeIdentifier);
156
+ // If this selection hasn't been seen before, keep track of it.
157
+ if (!flattenedSelection) {
158
+ flattenedSelections.set(nodeIdentifier, selection);
159
+ return;
160
+ }
161
+ // Otherwise a similar selection exists which should be merged.
162
+ hasFlattened = true;
163
+ if (flattenedSelection.kind === 'InlineFragment') {
164
+ if (selection.kind !== 'InlineFragment') {
165
+ throw createCompilerError(
166
+ `FlattenTransform: Expected an InlineFragment, got a '${selection.kind}'`,
167
+ [selection.loc],
168
+ );
169
+ }
170
+ flattenedSelections.set(nodeIdentifier, {
171
+ ...flattenedSelection,
172
+ selections: mergeSelections(
173
+ schema,
174
+ flattenedSelection,
175
+ selection,
176
+ state,
177
+ selection.typeCondition,
178
+ ),
179
+ });
180
+ } else if (flattenedSelection.kind === 'Condition') {
181
+ if (selection.kind !== 'Condition') {
182
+ throw createCompilerError(
183
+ `FlattenTransform: Expected a Condition, got a '${selection.kind}'`,
184
+ [selection.loc],
185
+ );
186
+ }
187
+ flattenedSelections.set(nodeIdentifier, {
188
+ ...flattenedSelection,
189
+ selections: mergeSelections(
190
+ schema,
191
+ flattenedSelection,
192
+ selection,
193
+ state,
194
+ type,
195
+ ),
196
+ });
197
+ } else if (flattenedSelection.kind === 'ClientExtension') {
198
+ if (selection.kind !== 'ClientExtension') {
199
+ throw createCompilerError(
200
+ `FlattenTransform: Expected a ClientExtension, got a '${selection.kind}'`,
201
+ [selection.loc],
202
+ );
203
+ }
204
+ flattenedSelections.set(nodeIdentifier, {
205
+ ...flattenedSelection,
206
+ selections: mergeSelections(
207
+ schema,
208
+ flattenedSelection,
209
+ selection,
210
+ state,
211
+ type,
212
+ ),
213
+ });
214
+ } else if (flattenedSelection.kind === 'FragmentSpread') {
215
+ // Ignore duplicate fragment spreads.
216
+ } else if (flattenedSelection.kind === 'ModuleImport') {
217
+ if (selection.kind !== 'ModuleImport') {
218
+ throw createCompilerError(
219
+ `FlattenTransform: Expected a ModuleImport, got a '${selection.kind}'`,
220
+ [selection.loc],
221
+ );
222
+ }
223
+ if (
224
+ selection.name !== flattenedSelection.name ||
225
+ selection.module !== flattenedSelection.module ||
226
+ selection.key !== flattenedSelection.key
227
+ ) {
228
+ throw createUserError(
229
+ 'Found conflicting @module selections: use a unique alias on the ' +
230
+ 'parent fields.',
231
+ [selection.loc, flattenedSelection.loc],
232
+ );
233
+ }
234
+ flattenedSelections.set(nodeIdentifier, {
235
+ ...flattenedSelection,
236
+ selections: mergeSelections(
237
+ schema,
238
+ flattenedSelection,
239
+ selection,
240
+ state,
241
+ type,
242
+ ),
243
+ });
244
+ } else if (flattenedSelection.kind === 'Defer') {
245
+ if (selection.kind !== 'Defer') {
246
+ throw createCompilerError(
247
+ `FlattenTransform: Expected a Defer, got a '${selection.kind}'`,
248
+ [selection.loc],
249
+ );
250
+ }
251
+ flattenedSelections.set(nodeIdentifier, {
252
+ kind: 'Defer',
253
+ ...flattenedSelection,
254
+ selections: mergeSelections(
255
+ schema,
256
+ flattenedSelection,
257
+ selection,
258
+ state,
259
+ type,
260
+ ),
261
+ });
262
+ } else if (flattenedSelection.kind === 'Stream') {
263
+ if (selection.kind !== 'Stream') {
264
+ throw createCompilerError(
265
+ `FlattenTransform: Expected a Stream, got a '${selection.kind}'`,
266
+ [selection.loc],
267
+ );
268
+ }
269
+ flattenedSelections.set(nodeIdentifier, {
270
+ kind: 'Stream',
271
+ ...flattenedSelection,
272
+ selections: mergeSelections(
273
+ schema,
274
+ flattenedSelection,
275
+ selection,
276
+ state,
277
+ type,
278
+ ),
279
+ });
280
+ } else if (flattenedSelection.kind === 'LinkedField') {
281
+ if (selection.kind !== 'LinkedField') {
282
+ throw createCompilerError(
283
+ `FlattenTransform: Expected a LinkedField, got a '${selection.kind}'`,
284
+ [selection.loc],
285
+ );
286
+ }
287
+ assertUniqueArgsForAlias(selection, flattenedSelection);
288
+ // NOTE: not using object spread here as this code is pretty hot
289
+ flattenedSelections.set(nodeIdentifier, {
290
+ kind: 'LinkedField',
291
+ alias: flattenedSelection.alias,
292
+ args: flattenedSelection.args,
293
+ connection: flattenedSelection.connection || selection.connection,
294
+ directives: flattenedSelection.directives,
295
+ handles: mergeHandles(flattenedSelection, selection),
296
+ loc: flattenedSelection.loc,
297
+ metadata: flattenedSelection.metadata,
298
+ name: flattenedSelection.name,
299
+ selections: mergeSelections(
300
+ schema,
301
+ flattenedSelection,
302
+ selection,
303
+ state,
304
+ selection.type,
305
+ ),
306
+ type: flattenedSelection.type,
307
+ });
308
+ } else if (flattenedSelection.kind === 'ScalarField') {
309
+ if (selection.kind !== 'ScalarField') {
310
+ throw createCompilerError(
311
+ `FlattenTransform: Expected a ScalarField, got a '${selection.kind}'`,
312
+ [selection.loc],
313
+ );
314
+ }
315
+ assertUniqueArgsForAlias(selection, flattenedSelection);
316
+ if (selection.handles && selection.handles.length > 0) {
317
+ flattenedSelections.set(nodeIdentifier, {
318
+ kind: 'ScalarField',
319
+ ...flattenedSelection,
320
+ handles: mergeHandles(selection, flattenedSelection),
321
+ });
322
+ }
323
+ } else if (flattenedSelection.kind === 'InlineDataFragmentSpread') {
324
+ throw createCompilerError(
325
+ 'FlattenTransform: did not expect an InlineDataFragmentSpread node. ' +
326
+ 'Only expecting InlineDataFragmentSpread in reader ASTs and this ' +
327
+ 'transform to run only on normalization ASTs.',
328
+ [selection.loc],
329
+ );
330
+ } else {
331
+ (flattenedSelection.kind: empty);
332
+ throw createCompilerError(
333
+ `FlattenTransform: Unknown kind '${flattenedSelection.kind}'`,
334
+ );
335
+ }
336
+ });
337
+ return hasFlattened;
338
+ }
339
+
340
+ /**
341
+ * @private
342
+ */
343
+ function mergeSelections(
344
+ schema: Schema,
345
+ nodeA: Node,
346
+ nodeB: Node,
347
+ state: State,
348
+ type: TypeID,
349
+ ): $ReadOnlyArray<Selection> {
350
+ const flattenedSelections = new Map();
351
+ flattenSelectionsInto(schema, flattenedSelections, nodeA, state, type);
352
+ flattenSelectionsInto(schema, flattenedSelections, nodeB, state, type);
353
+ return Array.from(flattenedSelections.values());
354
+ }
355
+
356
+ /**
357
+ * @private
358
+ * TODO(T19327202) This is redundant with OverlappingFieldsCanBeMergedRule once
359
+ * it can be enabled.
360
+ */
361
+ function assertUniqueArgsForAlias(field: Field, otherField: Field): void {
362
+ if (!areEqualFields(field, otherField)) {
363
+ throw createUserError(
364
+ 'Expected all fields on the same parent with the name or alias ' +
365
+ `'${field.alias}' to have the same name and arguments.`,
366
+ [field.loc, otherField.loc],
367
+ );
368
+ }
369
+ }
370
+
371
+ /**
372
+ * @private
373
+ */
374
+ function shouldFlattenInlineFragment(
375
+ schema: Schema,
376
+ fragment: InlineFragment,
377
+ state: State,
378
+ type: TypeID,
379
+ ): boolean {
380
+ return (
381
+ (schema.areEqualTypes(fragment.typeCondition, schema.getRawType(type)) &&
382
+ (state.isForCodegen || fragment.directives.length === 0)) ||
383
+ (state.isForCodegen && schema.isAbstractType(fragment.typeCondition))
384
+ );
385
+ }
386
+
387
+ /**
388
+ * @private
389
+ *
390
+ * Verify that two fields are equal in all properties other than their
391
+ * selections.
392
+ */
393
+ function areEqualFields(thisField: Field, thatField: Field): boolean {
394
+ return (
395
+ thisField.kind === thatField.kind &&
396
+ thisField.name === thatField.name &&
397
+ thisField.alias === thatField.alias &&
398
+ areEqualArgs(thisField.args, thatField.args)
399
+ );
400
+ }
401
+
402
+ /**
403
+ * Verify that two sets of arguments are equivalent - same argument names
404
+ * and values. Notably this ignores the types of arguments and values, which
405
+ * may not always be inferred identically.
406
+ */
407
+ function areEqualArgs(
408
+ thisArgs: $ReadOnlyArray<Argument>,
409
+ thatArgs: $ReadOnlyArray<Argument>,
410
+ ): boolean {
411
+ return (
412
+ thisArgs.length === thatArgs.length &&
413
+ thisArgs.every((thisArg, index) => {
414
+ const thatArg = thatArgs[index];
415
+ return (
416
+ thisArg.name === thatArg.name &&
417
+ thisArg.value.kind === thatArg.value.kind &&
418
+ (thisArg.value: any).variableName ===
419
+ (thatArg.value: any).variableName &&
420
+ areEqual((thisArg.value: any).value, (thatArg.value: any).value)
421
+ );
422
+ })
423
+ );
424
+ }
425
+
426
+ /**
427
+ * @private
428
+ */
429
+ function mergeHandles<T: LinkedField | ScalarField>(
430
+ nodeA: T,
431
+ nodeB: T,
432
+ ): ?$ReadOnlyArray<Handle> {
433
+ if (!nodeA.handles) {
434
+ return nodeB.handles;
435
+ }
436
+ if (!nodeB.handles) {
437
+ return nodeA.handles;
438
+ }
439
+ const uniqueItems = new Map();
440
+ nodeA.handles
441
+ .concat(nodeB.handles)
442
+ .forEach(item => uniqueItems.set(item.name + item.key, item));
443
+ return Array.from(uniqueItems.values());
444
+ }
445
+
446
+ function transformWithOptions(
447
+ options: FlattenOptions,
448
+ ): (context: CompilerContext) => CompilerContext {
449
+ return function flattenTransform(context: CompilerContext): CompilerContext {
450
+ return flattenTransformImpl(context, options);
451
+ };
452
+ }
453
+
454
+ module.exports = {
455
+ transformWithOptions,
456
+ };
@@ -0,0 +1,134 @@
1
+ /**
2
+ * Copyright (c) Facebook, Inc. and its 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
+ */
10
+
11
+ // flowlint ambiguous-object-type:error
12
+
13
+ 'use strict';
14
+
15
+ const IRTransformer = require('../core/IRTransformer');
16
+ const SchemaUtils = require('../core/SchemaUtils');
17
+
18
+ const {hasUnaliasedSelection} = require('./TransformUtils');
19
+
20
+ import type CompilerContext from '../core/CompilerContext';
21
+ import type {InlineFragment, LinkedField, ScalarField} from '../core/IR';
22
+ import type {CompositeTypeID} from '../core/Schema';
23
+ const {generateIDField} = SchemaUtils;
24
+
25
+ const ID = 'id';
26
+ const NODE_TYPE = 'Node';
27
+
28
+ type State = {idField: ScalarField, ...};
29
+
30
+ /**
31
+ * A transform that adds an `id` field on any type that has an id field but
32
+ * where there is no unaliased `id` selection.
33
+ */
34
+ function generateIDFieldTransform(context: CompilerContext): CompilerContext {
35
+ const schema = context.getSchema();
36
+ const idType = schema.expectIdType();
37
+ const idField = generateIDField(idType);
38
+ const state = {
39
+ idField,
40
+ };
41
+ return IRTransformer.transform(
42
+ context,
43
+ {
44
+ LinkedField: visitLinkedField,
45
+ },
46
+ () => state,
47
+ );
48
+ }
49
+
50
+ function visitLinkedField(field: LinkedField, state: State): LinkedField {
51
+ const transformedNode = this.traverse(field, state);
52
+
53
+ // If the field already has an unaliased `id` field, do nothing
54
+ if (hasUnaliasedSelection(field, ID)) {
55
+ return transformedNode;
56
+ }
57
+
58
+ const context: CompilerContext = this.getContext();
59
+ const schema = context.getSchema();
60
+ const unmodifiedType = schema.assertCompositeType(
61
+ schema.getRawType(field.type),
62
+ );
63
+
64
+ // If the field type has an `id` subfield add an `id` selection
65
+ if (
66
+ schema.canHaveSelections(unmodifiedType) &&
67
+ schema.hasId(unmodifiedType)
68
+ ) {
69
+ return {
70
+ ...transformedNode,
71
+ selections: [...transformedNode.selections, state.idField],
72
+ };
73
+ }
74
+
75
+ // If the field type is abstract, then generate a `... on Node { id }`
76
+ // fragment if *any* concrete type implements Node. Then generate a
77
+ // `... on PossibleType { id }` for every concrete type that does *not*
78
+ // implement `Node`
79
+ const nodeType = schema.getTypeFromString(NODE_TYPE);
80
+ if (!nodeType) {
81
+ return transformedNode;
82
+ }
83
+
84
+ const nodeInterface = schema.assertInterfaceType(nodeType);
85
+
86
+ if (schema.isAbstractType(unmodifiedType)) {
87
+ const selections = [...transformedNode.selections];
88
+ if (schema.mayImplement(unmodifiedType, nodeInterface)) {
89
+ selections.push(buildIDFragment(nodeInterface, state.idField));
90
+ }
91
+ schema
92
+ .getPossibleTypes(schema.assertAbstractType(unmodifiedType))
93
+ .forEach(possibleType => {
94
+ if (
95
+ !schema.implementsInterface(
96
+ schema.assertCompositeType(possibleType),
97
+ nodeInterface,
98
+ ) &&
99
+ schema.hasId(possibleType)
100
+ ) {
101
+ selections.push(buildIDFragment(possibleType, state.idField));
102
+ }
103
+ });
104
+ return {
105
+ ...transformedNode,
106
+ selections,
107
+ };
108
+ }
109
+
110
+ return transformedNode;
111
+ }
112
+
113
+ /**
114
+ * @internal
115
+ *
116
+ * Returns IR for `... on FRAGMENT_TYPE { id }`
117
+ */
118
+ function buildIDFragment(
119
+ fragmentType: CompositeTypeID,
120
+ idField: ScalarField,
121
+ ): InlineFragment {
122
+ return {
123
+ kind: 'InlineFragment',
124
+ directives: [],
125
+ loc: {kind: 'Generated'},
126
+ metadata: null,
127
+ selections: [idField],
128
+ typeCondition: fragmentType,
129
+ };
130
+ }
131
+
132
+ module.exports = {
133
+ transform: generateIDFieldTransform,
134
+ };
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Copyright (c) Facebook, Inc. and its 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
+ */
10
+
11
+ // flowlint ambiguous-object-type:error
12
+
13
+ 'use strict';
14
+
15
+ const IRTransformer = require('../core/IRTransformer');
16
+
17
+ const {hasUnaliasedSelection} = require('./TransformUtils');
18
+
19
+ import type CompilerContext from '../core/CompilerContext';
20
+ import type {LinkedField, ScalarField} from '../core/IR';
21
+ import type {Schema} from '../core/Schema';
22
+
23
+ const TYPENAME_KEY = '__typename';
24
+
25
+ type State = {typenameField: ScalarField, ...};
26
+
27
+ let cache = new Map();
28
+
29
+ /**
30
+ * A transform that adds `__typename` field on any `LinkedField` of a union or
31
+ * interface type where there is no unaliased `__typename` selection.
32
+ */
33
+ function generateTypeNameTransform(context: CompilerContext): CompilerContext {
34
+ cache = new Map();
35
+ const schema = context.getSchema();
36
+ const typenameField: ScalarField = {
37
+ kind: 'ScalarField',
38
+ alias: TYPENAME_KEY,
39
+ args: [],
40
+ directives: [],
41
+ handles: null,
42
+ loc: {kind: 'Generated'},
43
+ metadata: null,
44
+ name: TYPENAME_KEY,
45
+ type: schema.expectStringType(),
46
+ };
47
+ const state = {
48
+ typenameField,
49
+ };
50
+ return IRTransformer.transform(
51
+ context,
52
+ {
53
+ LinkedField: visitLinkedField,
54
+ },
55
+ () => state,
56
+ );
57
+ }
58
+
59
+ function visitLinkedField(field: LinkedField, state: State): LinkedField {
60
+ const schema: Schema = this.getContext().getSchema();
61
+ let transformedNode = cache.get(field);
62
+ if (transformedNode != null) {
63
+ return transformedNode;
64
+ }
65
+ transformedNode = (this.traverse(field, state): LinkedField);
66
+ if (
67
+ schema.isAbstractType(schema.getRawType(transformedNode.type)) &&
68
+ !hasUnaliasedSelection(transformedNode, TYPENAME_KEY)
69
+ ) {
70
+ transformedNode = {
71
+ ...transformedNode,
72
+ selections: [state.typenameField, ...transformedNode.selections],
73
+ };
74
+ }
75
+ cache.set(field, transformedNode);
76
+ return transformedNode;
77
+ }
78
+
79
+ module.exports = {
80
+ transform: generateTypeNameTransform,
81
+ };