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,1759 @@
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
8
+ * @format
9
+ */
10
+
11
+ // flowlint ambiguous-object-type:error
12
+
13
+ 'use strict';
14
+
15
+ const Profiler = require('./GraphQLCompilerProfiler');
16
+
17
+ const orList = require('../util/orList');
18
+ const partitionArray = require('../util/partitionArray');
19
+
20
+ const {DEFAULT_HANDLE_KEY} = require('../util/DefaultHandleKey');
21
+ const {
22
+ createCompilerError,
23
+ createUserError,
24
+ eachWithCombinedError,
25
+ } = require('./CompilerError');
26
+ const {isExecutableDefinitionAST} = require('./SchemaUtils');
27
+ const {getFieldDefinitionLegacy} = require('./getFieldDefinition');
28
+ const {parse: parseGraphQL, parseType, print, Source} = require('graphql');
29
+
30
+ import type {
31
+ Argument,
32
+ ArgumentValue,
33
+ Condition,
34
+ Directive,
35
+ Field,
36
+ Fragment,
37
+ FragmentSpread,
38
+ Handle,
39
+ InlineFragment,
40
+ LocalArgumentDefinition,
41
+ Location,
42
+ Root,
43
+ Selection,
44
+ Variable,
45
+ } from './IR';
46
+ import type {
47
+ CompositeTypeID,
48
+ Argument as FieldArgument,
49
+ FieldID,
50
+ InputTypeID,
51
+ Schema,
52
+ TypeID,
53
+ } from './Schema';
54
+ import type {GetFieldDefinitionFn} from './getFieldDefinition';
55
+ import type {
56
+ ASTNode,
57
+ ArgumentNode,
58
+ BooleanValueNode,
59
+ DefinitionNode,
60
+ DirectiveLocationEnum,
61
+ DirectiveNode,
62
+ EnumValueNode,
63
+ FieldNode,
64
+ FloatValueNode,
65
+ FragmentDefinitionNode,
66
+ FragmentSpreadNode,
67
+ InlineFragmentNode,
68
+ IntValueNode,
69
+ ListValueNode,
70
+ Location as ASTLocation,
71
+ ObjectValueNode,
72
+ OperationDefinitionNode,
73
+ SelectionSetNode,
74
+ StringValueNode,
75
+ TypeNode,
76
+ ValueNode,
77
+ VariableNode,
78
+ } from 'graphql';
79
+
80
+ type ASTDefinitionNode = FragmentDefinitionNode | OperationDefinitionNode;
81
+ type NonNullLiteralValueNode =
82
+ | IntValueNode
83
+ | FloatValueNode
84
+ | StringValueNode
85
+ | BooleanValueNode
86
+ | EnumValueNode
87
+ | ListValueNode
88
+ | ObjectValueNode;
89
+
90
+ type VariableDefinitions = Map<string, VariableDefinition>;
91
+ type VariableDefinition = {|
92
+ ast: ASTNode,
93
+ name: string,
94
+ defaultValue: mixed,
95
+ type: InputTypeID,
96
+ defined: boolean,
97
+ |};
98
+
99
+ type UnknownVariable = {|
100
+ ast: VariableNode,
101
+ type: ?TypeID,
102
+ |};
103
+
104
+ const ARGUMENT_DEFINITIONS = 'argumentDefinitions';
105
+ const ARGUMENTS = 'arguments';
106
+ const DEPRECATED_UNCHECKED_ARGUMENTS = 'uncheckedArguments_DEPRECATED';
107
+ const DIRECTIVE_WHITELIST: $ReadOnlySet<string> = new Set([
108
+ ARGUMENT_DEFINITIONS,
109
+ DEPRECATED_UNCHECKED_ARGUMENTS,
110
+ ARGUMENTS,
111
+ ]);
112
+
113
+ /**
114
+ * @internal
115
+ *
116
+ * This directive is not intended for use by developers directly. To set a field
117
+ * handle in product code use a compiler plugin.
118
+ */
119
+ const CLIENT_FIELD = '__clientField';
120
+ const CLIENT_FIELD_HANDLE = 'handle';
121
+ const CLIENT_FIELD_KEY = 'key';
122
+ const CLIENT_FIELD_FILTERS = 'filters';
123
+
124
+ const INCLUDE = 'include';
125
+ const SKIP = 'skip';
126
+ const IF = 'if';
127
+
128
+ /**
129
+ * Transforms GraphQL text into Relay Compiler's internal, strongly-typed
130
+ * intermediate representation (IR).
131
+ */
132
+ function parse(
133
+ schema: Schema,
134
+ text: string,
135
+ filename?: string,
136
+ ): $ReadOnlyArray<Root | Fragment> {
137
+ const ast = parseGraphQL(new Source(text, filename));
138
+ const parser = new RelayParser(schema.extend(ast), ast.definitions);
139
+ return parser.transform();
140
+ }
141
+
142
+ /**
143
+ * Transforms untyped GraphQL parse trees (ASTs) into Relay Compiler's
144
+ * internal, strongly-typed intermediate representation (IR).
145
+ */
146
+ function transform(
147
+ schema: Schema,
148
+ definitions: $ReadOnlyArray<DefinitionNode>,
149
+ ): $ReadOnlyArray<Root | Fragment> {
150
+ return Profiler.run('RelayParser.transform', () => {
151
+ const parser = new RelayParser(schema, definitions);
152
+ return parser.transform();
153
+ });
154
+ }
155
+
156
+ /**
157
+ * @private
158
+ */
159
+ class RelayParser {
160
+ _definitions: Map<string, ASTDefinitionNode>;
161
+ _getFieldDefinition: GetFieldDefinitionFn;
162
+ +_schema: Schema;
163
+
164
+ constructor(schema: Schema, definitions: $ReadOnlyArray<DefinitionNode>) {
165
+ this._definitions = new Map();
166
+ // leaving this configurable to make it easy to experiment w changing later
167
+ this._getFieldDefinition = getFieldDefinitionLegacy;
168
+ this._schema = schema;
169
+
170
+ const duplicated = new Set();
171
+ definitions.forEach(def => {
172
+ if (isExecutableDefinitionAST(def)) {
173
+ const name = getName(def);
174
+ if (this._definitions.has(name)) {
175
+ duplicated.add(name);
176
+ return;
177
+ }
178
+ this._definitions.set(name, def);
179
+ }
180
+ });
181
+ if (duplicated.size) {
182
+ throw createUserError(
183
+ 'RelayParser: Encountered duplicate definitions for one or more ' +
184
+ 'documents: each document must have a unique name. Duplicated documents:\n' +
185
+ Array.from(duplicated, name => `- ${name}`).join('\n'),
186
+ );
187
+ }
188
+ }
189
+
190
+ transform(): $ReadOnlyArray<Root | Fragment> {
191
+ const nodes = [];
192
+ const entries = new Map();
193
+ // Construct a mapping of name to definition ast + variable definitions.
194
+ // This allows the subsequent AST -> IR tranformation to reference the
195
+ // defined arguments of referenced fragments.
196
+ eachWithCombinedError(this._definitions, ([name, definition]) => {
197
+ const variableDefinitions = this._buildArgumentDefinitions(definition);
198
+ entries.set(name, {definition, variableDefinitions});
199
+ });
200
+ // Convert the ASTs to IR.
201
+ eachWithCombinedError(
202
+ entries.values(),
203
+ ({definition, variableDefinitions}) => {
204
+ const node = parseDefinition(
205
+ this._schema,
206
+ this._getFieldDefinition,
207
+ entries,
208
+ definition,
209
+ variableDefinitions,
210
+ );
211
+ nodes.push(node);
212
+ },
213
+ );
214
+ return nodes;
215
+ }
216
+
217
+ /**
218
+ * Constructs a mapping of variable names to definitions for the given
219
+ * operation/fragment definition.
220
+ */
221
+ _buildArgumentDefinitions(
222
+ definition: ASTDefinitionNode,
223
+ ): VariableDefinitions {
224
+ switch (definition.kind) {
225
+ case 'OperationDefinition':
226
+ return this._buildOperationArgumentDefinitions(definition);
227
+ case 'FragmentDefinition':
228
+ return this._buildFragmentArgumentDefinitions(definition);
229
+ default:
230
+ (definition: empty);
231
+ throw createCompilerError(`Unexpected ast kind '${definition.kind}'.`, [
232
+ definition,
233
+ ]);
234
+ }
235
+ }
236
+
237
+ /**
238
+ * Constructs a mapping of variable names to definitions using the
239
+ * variables defined in `@argumentDefinitions`.
240
+ */
241
+ _buildFragmentArgumentDefinitions(
242
+ fragment: FragmentDefinitionNode,
243
+ ): VariableDefinitions {
244
+ const variableDirectives = (fragment.directives || []).filter(
245
+ directive => getName(directive) === ARGUMENT_DEFINITIONS,
246
+ );
247
+ if (!variableDirectives.length) {
248
+ return new Map();
249
+ }
250
+ if (variableDirectives.length !== 1) {
251
+ throw createUserError(
252
+ `Directive @${ARGUMENT_DEFINITIONS} may be defined at most once per ` +
253
+ 'fragment.',
254
+ null,
255
+ variableDirectives,
256
+ );
257
+ }
258
+ const variableDirective = variableDirectives[0];
259
+ // $FlowIssue: refining directly on `variableDirective.arguments` doesn't
260
+ // work, below accesses all report arguments could still be null/undefined.
261
+ const args = variableDirective.arguments;
262
+ if (variableDirective == null || !Array.isArray(args)) {
263
+ return new Map();
264
+ }
265
+ if (!args.length) {
266
+ throw createUserError(
267
+ `Directive @${ARGUMENT_DEFINITIONS} requires arguments: remove the ` +
268
+ 'directive to skip defining local variables for this fragment.',
269
+ null,
270
+ [variableDirective],
271
+ );
272
+ }
273
+ const variables = new Map();
274
+ args.forEach(arg => {
275
+ const argName = getName(arg);
276
+ const previousVariable = variables.get(argName);
277
+ if (previousVariable != null) {
278
+ throw createUserError(
279
+ `Duplicate definition for variable '\$${argName}'.`,
280
+ null,
281
+ [previousVariable.ast, arg],
282
+ );
283
+ }
284
+ if (arg.value.kind !== 'ObjectValue') {
285
+ throw createUserError(
286
+ `Expected definition for variable '\$${argName}' to be an object ` +
287
+ "with the shape: '{type: string, defaultValue?: mixed}.",
288
+ null,
289
+ [arg.value],
290
+ );
291
+ }
292
+ let defaultValueNode;
293
+ let typeString;
294
+ arg.value.fields.forEach(field => {
295
+ const name = getName(field);
296
+ if (name === 'type') {
297
+ typeString = transformLiteralValue(field.value, field);
298
+ } else if (name === 'defaultValue') {
299
+ defaultValueNode = field.value;
300
+ } else {
301
+ throw createUserError(
302
+ `Expected definition for variable '\$${argName}' to be an object ` +
303
+ "with the shape: '{type: string, defaultValue?: mixed}.",
304
+ null,
305
+ [arg.value],
306
+ );
307
+ }
308
+ });
309
+ if (typeof typeString !== 'string') {
310
+ throw createUserError(
311
+ `Expected definition for variable '\$${argName}' to be an object ` +
312
+ "with the shape: '{type: string, defaultValue?: mixed}.",
313
+ null,
314
+ [arg.value],
315
+ );
316
+ }
317
+ const typeFromAST = this._schema.getTypeFromAST(parseType(typeString));
318
+ if (typeFromAST == null) {
319
+ throw createUserError(
320
+ `Unknown type "${typeString}" referenced in the argument definitions.`,
321
+ null,
322
+ [arg],
323
+ );
324
+ }
325
+ const type = this._schema.asInputType(typeFromAST);
326
+ if (type == null) {
327
+ throw createUserError(
328
+ `Expected type "${typeString}" to be an input type in the "${arg.name.value}" argument definitions.`,
329
+ null,
330
+ [arg.value],
331
+ );
332
+ }
333
+ const defaultValue =
334
+ defaultValueNode != null
335
+ ? transformValue(
336
+ this._schema,
337
+ defaultValueNode,
338
+ type,
339
+ variableAst => {
340
+ throw createUserError(
341
+ "Expected 'defaultValue' to be a literal, got a variable.",
342
+ null,
343
+ [variableAst],
344
+ );
345
+ },
346
+ {nonStrictEnums: true},
347
+ )
348
+ : null;
349
+ if (defaultValue != null && defaultValue.kind !== 'Literal') {
350
+ throw createUserError(
351
+ "Expected 'defaultValue' to be a literal, got a variable.",
352
+ [defaultValue.loc],
353
+ );
354
+ }
355
+ variables.set(argName, {
356
+ ast: arg,
357
+ defaultValue: defaultValue?.value ?? null,
358
+ defined: true,
359
+ name: argName,
360
+ type,
361
+ });
362
+ });
363
+ return variables;
364
+ }
365
+
366
+ /**
367
+ * Constructs a mapping of variable names to definitions using the
368
+ * standard GraphQL syntax for variable definitions.
369
+ */
370
+ _buildOperationArgumentDefinitions(
371
+ operation: OperationDefinitionNode,
372
+ ): VariableDefinitions {
373
+ const schema = this._schema;
374
+ const variableDefinitions = new Map();
375
+ (operation.variableDefinitions || []).forEach(def => {
376
+ const name = getName(def.variable);
377
+ const typeFromAST = schema.getTypeFromAST(def.type);
378
+ if (typeFromAST == null) {
379
+ throw createUserError(
380
+ `Unknown type: '${getTypeName(def.type)}'.`,
381
+ null,
382
+ [def.type],
383
+ );
384
+ }
385
+
386
+ const type = schema.asInputType(typeFromAST);
387
+ if (type == null) {
388
+ throw createUserError(
389
+ `Expected type "${getTypeName(def.type)}" to be an input type.`,
390
+ null,
391
+ [def.type],
392
+ );
393
+ }
394
+
395
+ const defaultValue = def.defaultValue
396
+ ? transformLiteralValue(def.defaultValue, def)
397
+ : null;
398
+ const previousDefinition = variableDefinitions.get(name);
399
+ if (previousDefinition != null) {
400
+ throw createUserError(
401
+ `Duplicate definition for variable '\$${name}'.`,
402
+ null,
403
+ [previousDefinition.ast, def],
404
+ );
405
+ }
406
+ variableDefinitions.set(name, {
407
+ ast: def,
408
+ defaultValue,
409
+ defined: true,
410
+ name,
411
+ type,
412
+ });
413
+ });
414
+ return variableDefinitions;
415
+ }
416
+ }
417
+
418
+ /**
419
+ * @private
420
+ */
421
+ function parseDefinition(
422
+ schema: Schema,
423
+ getFieldDefinition: GetFieldDefinitionFn,
424
+ entries: Map<
425
+ string,
426
+ {|definition: ASTDefinitionNode, variableDefinitions: VariableDefinitions|},
427
+ >,
428
+ definition: ASTDefinitionNode,
429
+ variableDefinitions: VariableDefinitions,
430
+ ): Fragment | Root {
431
+ const parser = new GraphQLDefinitionParser(
432
+ schema,
433
+ getFieldDefinition,
434
+ entries,
435
+ definition,
436
+ variableDefinitions,
437
+ );
438
+ return parser.transform();
439
+ }
440
+
441
+ /**
442
+ * @private
443
+ */
444
+ class GraphQLDefinitionParser {
445
+ _definition: ASTDefinitionNode;
446
+ _entries: Map<
447
+ string,
448
+ {|
449
+ definition: ASTDefinitionNode,
450
+ variableDefinitions: VariableDefinitions,
451
+ |},
452
+ >;
453
+ _getFieldDefinition: GetFieldDefinitionFn;
454
+ _schema: Schema;
455
+ _variableDefinitions: VariableDefinitions;
456
+ _unknownVariables: Map<string, UnknownVariable>;
457
+ _directiveLocations: ?Map<string, $ReadOnlyArray<DirectiveLocationEnum>>;
458
+
459
+ constructor(
460
+ schema: Schema,
461
+ getFieldDefinition: GetFieldDefinitionFn,
462
+ entries: Map<
463
+ string,
464
+ {|
465
+ definition: ASTDefinitionNode,
466
+ variableDefinitions: VariableDefinitions,
467
+ |},
468
+ >,
469
+ definition: ASTDefinitionNode,
470
+ variableDefinitions: VariableDefinitions,
471
+ ): void {
472
+ this._definition = definition;
473
+ this._entries = entries;
474
+ this._getFieldDefinition = getFieldDefinition;
475
+ this._schema = schema;
476
+ this._variableDefinitions = variableDefinitions;
477
+ this._unknownVariables = new Map();
478
+ }
479
+
480
+ transform(): Root | Fragment {
481
+ const definition = this._definition;
482
+ switch (definition.kind) {
483
+ case 'OperationDefinition':
484
+ return this._transformOperation(definition);
485
+ case 'FragmentDefinition':
486
+ return this._transformFragment(definition);
487
+ default:
488
+ (definition: empty);
489
+ throw createCompilerError(
490
+ `Unsupported definition type ${definition.kind}`,
491
+ [definition],
492
+ );
493
+ }
494
+ }
495
+
496
+ _recordAndVerifyVariableReference(
497
+ variable: VariableNode,
498
+ name: string,
499
+ usedAsType: ?TypeID,
500
+ ): void {
501
+ // Special case for variables used in @arguments where we currently
502
+ // aren't guaranteed to be able to resolve the type.
503
+ if (usedAsType == null) {
504
+ if (
505
+ !this._variableDefinitions.has(name) &&
506
+ !this._unknownVariables.has(name)
507
+ ) {
508
+ this._unknownVariables.set(name, {
509
+ ast: variable,
510
+ type: null,
511
+ });
512
+ }
513
+ return;
514
+ }
515
+ const variableDefinition = this._variableDefinitions.get(name);
516
+ if (variableDefinition != null) {
517
+ // If the variable is defined, all usages must be compatible
518
+ let effectiveType = variableDefinition.type;
519
+ if (variableDefinition.defaultValue != null) {
520
+ // If a default value is defined then it is guaranteed to be used
521
+ // at runtime such that the effective type of the variable is non-null
522
+ effectiveType = this._schema.getNonNullType(
523
+ this._schema.getNullableType(effectiveType),
524
+ );
525
+ }
526
+
527
+ if (!this._schema.isTypeSubTypeOf(effectiveType, usedAsType)) {
528
+ throw createUserError(
529
+ `Variable '\$${name}' was defined as type '${String(
530
+ variableDefinition.type,
531
+ )}' but used in a location expecting the type '${String(
532
+ usedAsType,
533
+ )}'`,
534
+ null,
535
+ [variableDefinition.ast, variable],
536
+ );
537
+ }
538
+ } else {
539
+ const previous = this._unknownVariables.get(name);
540
+
541
+ if (!previous || !previous.type) {
542
+ // No previous usage, current type is strongest
543
+ this._unknownVariables.set(name, {
544
+ ast: variable,
545
+ type: usedAsType,
546
+ });
547
+ } else {
548
+ const {ast: previousVariable, type: previousType} = previous;
549
+ if (
550
+ !(
551
+ this._schema.isTypeSubTypeOf(usedAsType, previousType) ||
552
+ this._schema.isTypeSubTypeOf(previousType, usedAsType)
553
+ )
554
+ ) {
555
+ throw createUserError(
556
+ `Variable '\$${name}' was used in locations expecting the conflicting types '${String(
557
+ previousType,
558
+ )}' and '${String(usedAsType)}'.`,
559
+ null,
560
+ [previousVariable, variable],
561
+ );
562
+ }
563
+
564
+ // If the new used type has stronger requirements, use that type as reference,
565
+ // otherwise keep referencing the previous type
566
+ if (this._schema.isTypeSubTypeOf(usedAsType, previousType)) {
567
+ this._unknownVariables.set(name, {
568
+ ast: variable,
569
+ type: usedAsType,
570
+ });
571
+ }
572
+ }
573
+ }
574
+ }
575
+
576
+ _getDirectiveLocations(): Map<string, $ReadOnlyArray<DirectiveLocationEnum>> {
577
+ if (!this._directiveLocations) {
578
+ const directiveDefs = this._schema.getDirectives();
579
+ this._directiveLocations = new Map();
580
+ for (const def of directiveDefs) {
581
+ this._directiveLocations.set(def.name, def.locations);
582
+ }
583
+ }
584
+ return this._directiveLocations;
585
+ }
586
+
587
+ _validateDirectivesLocation(
588
+ directives: ?$ReadOnlyArray<DirectiveNode>,
589
+ allowedLocaction: DirectiveLocationEnum,
590
+ ): void {
591
+ if (!directives || !directives.length) {
592
+ return;
593
+ }
594
+ const directiveLocs = this._getDirectiveLocations();
595
+ const mismatches = directives.filter(directive => {
596
+ const name = getName(directive);
597
+ if (DIRECTIVE_WHITELIST.has(name)) {
598
+ return false;
599
+ }
600
+ const locs = directiveLocs.get(name);
601
+ if (locs == null) {
602
+ throw createUserError(`Unknown directive '${name}'.`, null, [
603
+ directive,
604
+ ]);
605
+ }
606
+ return !locs.some(loc => loc === allowedLocaction);
607
+ });
608
+ if (mismatches.length) {
609
+ const invalidDirectives = mismatches
610
+ .map(directive => '@' + getName(directive))
611
+ .join(', ');
612
+ throw createUserError(
613
+ `Invalid directives ${invalidDirectives} found on ${allowedLocaction}.`,
614
+ null,
615
+ mismatches,
616
+ );
617
+ }
618
+ }
619
+
620
+ _transformFragment(fragment: FragmentDefinitionNode): Fragment {
621
+ const directives = this._transformDirectives(
622
+ (fragment.directives || []).filter(
623
+ directive => getName(directive) !== ARGUMENT_DEFINITIONS,
624
+ ),
625
+ 'FRAGMENT_DEFINITION',
626
+ );
627
+
628
+ const typeFromAST = this._schema.getTypeFromAST(fragment.typeCondition);
629
+ if (typeFromAST == null) {
630
+ throw createUserError(
631
+ `Fragment "${fragment.name.value}" cannot condition on unknown ` +
632
+ `type "${String(fragment.typeCondition.name.value)}".`,
633
+ null,
634
+ [fragment.typeCondition],
635
+ );
636
+ }
637
+
638
+ const type = this._schema.asCompositeType(typeFromAST);
639
+ if (type == null) {
640
+ throw createUserError(
641
+ `Fragment "${fragment.name.value}" cannot condition on non composite ` +
642
+ `type "${String(type)}".`,
643
+ null,
644
+ [fragment.typeCondition],
645
+ );
646
+ }
647
+
648
+ const selections = this._transformSelections(
649
+ fragment.selectionSet,
650
+ type,
651
+ fragment.typeCondition,
652
+ );
653
+ const argumentDefinitions = [
654
+ ...buildArgumentDefinitions(this._variableDefinitions),
655
+ ];
656
+ for (const [name, variableReference] of this._unknownVariables) {
657
+ argumentDefinitions.push({
658
+ kind: 'RootArgumentDefinition',
659
+ loc: buildLocation(variableReference.ast.loc),
660
+ name,
661
+ type: variableReference.type,
662
+ });
663
+ }
664
+ return {
665
+ kind: 'Fragment',
666
+ directives,
667
+ loc: buildLocation(fragment.loc),
668
+ metadata: null,
669
+ name: getName(fragment),
670
+ selections,
671
+ type,
672
+ // $FlowFixMe - could be null
673
+ argumentDefinitions,
674
+ };
675
+ }
676
+
677
+ _getLocationFromOperation(
678
+ definition: OperationDefinitionNode,
679
+ ): DirectiveLocationEnum {
680
+ switch (definition.operation) {
681
+ case 'query':
682
+ return 'QUERY';
683
+ case 'mutation':
684
+ return 'MUTATION';
685
+ case 'subscription':
686
+ return 'SUBSCRIPTION';
687
+ default:
688
+ (definition.operation: empty);
689
+ throw createCompilerError(
690
+ `Unknown operation type '${definition.operation}'.`,
691
+ null,
692
+ [definition],
693
+ );
694
+ }
695
+ }
696
+
697
+ _transformOperation(definition: OperationDefinitionNode): Root {
698
+ const name = getName(definition);
699
+ const directives = this._transformDirectives(
700
+ definition.directives || [],
701
+ this._getLocationFromOperation(definition),
702
+ );
703
+ let type: TypeID;
704
+ let operation;
705
+ const schema = this._schema;
706
+ switch (definition.operation) {
707
+ case 'query':
708
+ operation = 'query';
709
+ type = schema.expectQueryType();
710
+ break;
711
+ case 'mutation':
712
+ operation = 'mutation';
713
+ type = schema.expectMutationType();
714
+ break;
715
+ case 'subscription':
716
+ operation = 'subscription';
717
+ type = schema.expectSubscriptionType();
718
+ break;
719
+ default:
720
+ (definition.operation: empty);
721
+ throw createCompilerError(
722
+ `Unknown operation type '${definition.operation}'.`,
723
+ null,
724
+ [definition],
725
+ );
726
+ }
727
+ if (!definition.selectionSet) {
728
+ throw createUserError('Expected operation to have selections.', null, [
729
+ definition,
730
+ ]);
731
+ }
732
+ const selections = this._transformSelections(definition.selectionSet, type);
733
+ const argumentDefinitions = buildArgumentDefinitions(
734
+ this._variableDefinitions,
735
+ );
736
+ if (this._unknownVariables.size !== 0) {
737
+ throw createUserError(
738
+ `Query '${name}' references undefined variables.`,
739
+ null,
740
+ Array.from(
741
+ this._unknownVariables.values(),
742
+ variableReference => variableReference.ast,
743
+ ),
744
+ );
745
+ }
746
+ return {
747
+ kind: 'Root',
748
+ operation,
749
+ loc: buildLocation(definition.loc),
750
+ metadata: null,
751
+ name,
752
+ argumentDefinitions,
753
+ directives,
754
+ selections,
755
+ type,
756
+ };
757
+ }
758
+
759
+ _transformSelections(
760
+ selectionSet: SelectionSetNode,
761
+ parentType: TypeID,
762
+ parentTypeAST?: TypeNode,
763
+ ): $ReadOnlyArray<Selection> {
764
+ return selectionSet.selections.map(selection => {
765
+ let node;
766
+ if (selection.kind === 'Field') {
767
+ node = this._transformField(selection, parentType);
768
+ } else if (selection.kind === 'FragmentSpread') {
769
+ node = this._transformFragmentSpread(
770
+ selection,
771
+ parentType,
772
+ parentTypeAST,
773
+ );
774
+ } else if (selection.kind === 'InlineFragment') {
775
+ node = this._transformInlineFragment(
776
+ selection,
777
+ parentType,
778
+ parentTypeAST,
779
+ );
780
+ } else {
781
+ (selection.kind: empty);
782
+ throw createCompilerError(`Unknown ast kind '${selection.kind}'.`, [
783
+ selection,
784
+ ]);
785
+ }
786
+ const [conditions, directives] = this._splitConditions(node.directives);
787
+ const conditionalNodes = applyConditions(
788
+ conditions,
789
+ // $FlowFixMe(>=0.28.0)
790
+ [{...node, directives}],
791
+ );
792
+ if (conditionalNodes.length !== 1) {
793
+ throw createCompilerError(
794
+ 'Expected exactly one condition node.',
795
+ null,
796
+ selection.directives,
797
+ );
798
+ }
799
+ return conditionalNodes[0];
800
+ });
801
+ }
802
+
803
+ _transformInlineFragment(
804
+ fragment: InlineFragmentNode,
805
+ parentType: TypeID,
806
+ parentTypeAST: ?TypeNode,
807
+ ): InlineFragment {
808
+ const schema = this._schema;
809
+ let typeCondition =
810
+ fragment.typeCondition != null
811
+ ? schema.getTypeFromAST(fragment.typeCondition)
812
+ : parentType;
813
+
814
+ if (typeCondition == null) {
815
+ throw createUserError(
816
+ 'Inline fragments can only be on object, interface or union types' +
817
+ `, got unknown type '${getTypeName(fragment.typeCondition)}'.`,
818
+ null,
819
+ [fragment.typeCondition ?? fragment],
820
+ );
821
+ }
822
+ const typeConditionName = schema.getTypeString(typeCondition);
823
+ typeCondition = schema.asCompositeType(typeCondition);
824
+ if (typeCondition == null) {
825
+ throw createUserError(
826
+ 'Inline fragments can only be on object, interface or union types' +
827
+ `, got '${typeConditionName}'.`,
828
+ null,
829
+ [fragment.typeCondition ?? fragment],
830
+ );
831
+ }
832
+ const rawParentType = this._schema.assertCompositeType(
833
+ this._schema.getRawType(parentType),
834
+ );
835
+
836
+ checkFragmentSpreadTypeCompatibility(
837
+ this._schema,
838
+ typeCondition,
839
+ rawParentType,
840
+ null,
841
+ fragment.typeCondition,
842
+ parentTypeAST,
843
+ );
844
+
845
+ const directives = this._transformDirectives(
846
+ fragment.directives || [],
847
+ 'INLINE_FRAGMENT',
848
+ );
849
+ const selections = this._transformSelections(
850
+ fragment.selectionSet,
851
+ typeCondition,
852
+ fragment.typeCondition,
853
+ );
854
+ return {
855
+ kind: 'InlineFragment',
856
+ directives,
857
+ loc: buildLocation(fragment.loc),
858
+ metadata: null,
859
+ selections,
860
+ typeCondition,
861
+ };
862
+ }
863
+
864
+ _transformFragmentSpread(
865
+ fragmentSpread: FragmentSpreadNode,
866
+ parentType: TypeID,
867
+ parentTypeAST: ?TypeNode,
868
+ ): FragmentSpread {
869
+ const fragmentName = getName(fragmentSpread);
870
+ const [argumentDirectives, otherDirectives] = partitionArray(
871
+ fragmentSpread.directives || [],
872
+ directive => {
873
+ const name = getName(directive);
874
+ return name === ARGUMENTS || name === DEPRECATED_UNCHECKED_ARGUMENTS;
875
+ },
876
+ );
877
+ if (argumentDirectives.length > 1) {
878
+ throw createUserError(
879
+ `Directive @${ARGUMENTS} may be used at most once per a fragment spread.`,
880
+ null,
881
+ argumentDirectives,
882
+ );
883
+ }
884
+ const fragmentDefinition = this._entries.get(fragmentName);
885
+ if (fragmentDefinition == null) {
886
+ throw createUserError(`Unknown fragment '${fragmentName}'.`, null, [
887
+ fragmentSpread.name,
888
+ ]);
889
+ }
890
+
891
+ const fragmentTypeNode = getFragmentType(fragmentDefinition.definition);
892
+ const fragmentType = this._schema.assertCompositeType(
893
+ this._schema.expectTypeFromAST(fragmentTypeNode),
894
+ );
895
+ const rawParentType = this._schema.assertCompositeType(
896
+ this._schema.getRawType(parentType),
897
+ );
898
+
899
+ checkFragmentSpreadTypeCompatibility(
900
+ this._schema,
901
+ fragmentType,
902
+ rawParentType,
903
+ fragmentSpread.name.value,
904
+ fragmentSpread,
905
+ parentTypeAST,
906
+ );
907
+
908
+ const fragmentArgumentDefinitions = fragmentDefinition.variableDefinitions;
909
+ const argumentsDirective = argumentDirectives[0];
910
+ let args;
911
+ if (argumentsDirective != null) {
912
+ const isDeprecatedUncheckedArguments =
913
+ getName(argumentsDirective) === DEPRECATED_UNCHECKED_ARGUMENTS;
914
+ let hasInvalidArgument = false;
915
+ args = (argumentsDirective.arguments || []).map(arg => {
916
+ const argName = getName(arg);
917
+ const argValue = arg.value;
918
+ const argumentDefinition = fragmentArgumentDefinitions.get(argName);
919
+ const argumentType = argumentDefinition?.type ?? null;
920
+
921
+ if (argValue.kind === 'Variable') {
922
+ if (argumentDefinition == null && !isDeprecatedUncheckedArguments) {
923
+ throw createUserError(
924
+ `Variable @${ARGUMENTS} values are only supported when the ` +
925
+ `argument is defined with @${ARGUMENT_DEFINITIONS}. Check ` +
926
+ `the definition of fragment '${fragmentName}'.`,
927
+ null,
928
+ [arg.value, fragmentDefinition.definition],
929
+ );
930
+ }
931
+ hasInvalidArgument = hasInvalidArgument || argumentDefinition == null;
932
+ // TODO: check the type of the variable and use the type
933
+ return {
934
+ kind: 'Argument',
935
+ loc: buildLocation(arg.loc),
936
+ name: argName,
937
+ value: this._transformVariable(argValue, null),
938
+ type: null,
939
+ };
940
+ } else {
941
+ if (argumentType == null) {
942
+ throw createUserError(
943
+ `Literal @${ARGUMENTS} values are only supported when the ` +
944
+ `argument is defined with @${ARGUMENT_DEFINITIONS}. Check ` +
945
+ `the definition of fragment '${fragmentName}'.`,
946
+ null,
947
+ [arg.value, fragmentDefinition.definition],
948
+ );
949
+ }
950
+ const value = this._transformValue(argValue, argumentType);
951
+ return {
952
+ kind: 'Argument',
953
+ loc: buildLocation(arg.loc),
954
+ name: argName,
955
+ value,
956
+ type: argumentType,
957
+ };
958
+ }
959
+ });
960
+ if (isDeprecatedUncheckedArguments && !hasInvalidArgument) {
961
+ throw createUserError(
962
+ `Invalid use of @${DEPRECATED_UNCHECKED_ARGUMENTS}: all arguments ` +
963
+ `are defined, use @${ARGUMENTS} instead.`,
964
+ null,
965
+ [argumentsDirective],
966
+ );
967
+ }
968
+ }
969
+ const directives = this._transformDirectives(
970
+ otherDirectives,
971
+ 'FRAGMENT_SPREAD',
972
+ );
973
+ return {
974
+ kind: 'FragmentSpread',
975
+ args: args || [],
976
+ metadata: null,
977
+ loc: buildLocation(fragmentSpread.loc),
978
+ name: fragmentName,
979
+ directives,
980
+ };
981
+ }
982
+
983
+ _transformField(field: FieldNode, parentType: TypeID): Field {
984
+ const schema = this._schema;
985
+ const name = getName(field);
986
+ const fieldDef = this._getFieldDefinition(schema, parentType, name, field);
987
+ if (fieldDef == null) {
988
+ throw createUserError(
989
+ `Unknown field '${name}' on type '${schema.getTypeString(
990
+ parentType,
991
+ )}'.`,
992
+ null,
993
+ [field],
994
+ );
995
+ }
996
+ const alias = field.alias?.value ?? name;
997
+ const args = this._transformArguments(
998
+ field.arguments || [],
999
+ schema.getFieldArgs(fieldDef),
1000
+ fieldDef,
1001
+ );
1002
+ const [otherDirectives, clientFieldDirectives] = partitionArray(
1003
+ field.directives || [],
1004
+ directive => getName(directive) !== CLIENT_FIELD,
1005
+ );
1006
+ const directives = this._transformDirectives(otherDirectives, 'FIELD');
1007
+ const type = schema.getFieldType(fieldDef);
1008
+
1009
+ const handles = this._transformHandle(name, args, clientFieldDirectives);
1010
+ if (schema.isLeafType(schema.getRawType(type))) {
1011
+ if (
1012
+ field.selectionSet &&
1013
+ field.selectionSet.selections &&
1014
+ field.selectionSet.selections.length
1015
+ ) {
1016
+ throw createUserError(
1017
+ `Expected no selections for scalar field '${name}'.`,
1018
+ null,
1019
+ [field],
1020
+ );
1021
+ }
1022
+ return {
1023
+ kind: 'ScalarField',
1024
+ alias,
1025
+ args,
1026
+ directives,
1027
+ handles,
1028
+ loc: buildLocation(field.loc),
1029
+ metadata: null,
1030
+ name,
1031
+ type: schema.assertScalarFieldType(type),
1032
+ };
1033
+ } else {
1034
+ const selections = field.selectionSet
1035
+ ? this._transformSelections(field.selectionSet, type)
1036
+ : null;
1037
+ if (selections == null || selections.length === 0) {
1038
+ throw createUserError(
1039
+ `Expected at least one selection for non-scalar field '${name}' on type '${schema.getTypeString(
1040
+ type,
1041
+ )}'.`,
1042
+ null,
1043
+ [field],
1044
+ );
1045
+ }
1046
+ return {
1047
+ kind: 'LinkedField',
1048
+ alias,
1049
+ args,
1050
+ connection: false,
1051
+ directives,
1052
+ handles,
1053
+ loc: buildLocation(field.loc),
1054
+ metadata: null,
1055
+ name,
1056
+ selections,
1057
+ type: schema.assertLinkedFieldType(type),
1058
+ };
1059
+ }
1060
+ }
1061
+
1062
+ _transformHandle(
1063
+ fieldName: string,
1064
+ fieldArgs: $ReadOnlyArray<Argument>,
1065
+ clientFieldDirectives: $ReadOnlyArray<DirectiveNode>,
1066
+ ): ?$ReadOnlyArray<Handle> {
1067
+ let handles: ?Array<Handle> = null;
1068
+ clientFieldDirectives.forEach(clientFieldDirective => {
1069
+ const handleArgument = (clientFieldDirective.arguments || []).find(
1070
+ arg => getName(arg) === CLIENT_FIELD_HANDLE,
1071
+ );
1072
+ if (handleArgument) {
1073
+ let name = null;
1074
+ let key = DEFAULT_HANDLE_KEY;
1075
+ let filters = null;
1076
+ const maybeHandle = transformLiteralValue(
1077
+ handleArgument.value,
1078
+ handleArgument,
1079
+ );
1080
+ if (typeof maybeHandle !== 'string') {
1081
+ throw createUserError(
1082
+ `Expected a string literal argument for the @${CLIENT_FIELD} directive.`,
1083
+ null,
1084
+ [handleArgument.value],
1085
+ );
1086
+ }
1087
+ name = maybeHandle;
1088
+
1089
+ const keyArgument = (clientFieldDirective.arguments || []).find(
1090
+ arg => getName(arg) === CLIENT_FIELD_KEY,
1091
+ );
1092
+ if (keyArgument) {
1093
+ const maybeKey = transformLiteralValue(
1094
+ keyArgument.value,
1095
+ keyArgument,
1096
+ );
1097
+ if (typeof maybeKey !== 'string') {
1098
+ throw createUserError(
1099
+ `Expected a string literal argument for the @${CLIENT_FIELD} directive.`,
1100
+ null,
1101
+ [keyArgument.value],
1102
+ );
1103
+ }
1104
+ key = maybeKey;
1105
+ }
1106
+ const filtersArgument = (clientFieldDirective.arguments || []).find(
1107
+ arg => getName(arg) === CLIENT_FIELD_FILTERS,
1108
+ );
1109
+ if (filtersArgument) {
1110
+ const maybeFilters = transformLiteralValue(
1111
+ filtersArgument.value,
1112
+ filtersArgument,
1113
+ );
1114
+ if (
1115
+ !(
1116
+ Array.isArray(maybeFilters) &&
1117
+ maybeFilters.every(
1118
+ filter =>
1119
+ typeof filter === 'string' &&
1120
+ fieldArgs.some(fieldArg => fieldArg.name === filter),
1121
+ )
1122
+ )
1123
+ ) {
1124
+ throw createUserError(
1125
+ `Expected an array of argument names on field '${fieldName}'.`,
1126
+ null,
1127
+ [filtersArgument.value],
1128
+ );
1129
+ }
1130
+ // $FlowFixMe
1131
+ filters = (maybeFilters: Array<string>);
1132
+ }
1133
+ const dynamicKeyArgument = (clientFieldDirective.arguments || []).find(
1134
+ arg => getName(arg) === 'dynamicKey_UNSTABLE',
1135
+ );
1136
+ if (dynamicKeyArgument != null) {
1137
+ throw createUserError(
1138
+ 'Dynamic keys are only supported with @connection.',
1139
+ null,
1140
+ [dynamicKeyArgument.value],
1141
+ );
1142
+ }
1143
+ handles = handles || [];
1144
+ handles.push({name, key, filters, dynamicKey: null});
1145
+ }
1146
+ });
1147
+ return handles;
1148
+ }
1149
+
1150
+ _transformDirectives(
1151
+ directives: $ReadOnlyArray<DirectiveNode>,
1152
+ location: DirectiveLocationEnum,
1153
+ ): $ReadOnlyArray<Directive> {
1154
+ this._validateDirectivesLocation(directives, location);
1155
+ return directives.map(directive => {
1156
+ const name = getName(directive);
1157
+ const directiveDef = this._schema.getDirective(name);
1158
+ if (directiveDef == null) {
1159
+ throw createUserError(`Unknown directive '${name}'.`, null, [
1160
+ directive,
1161
+ ]);
1162
+ }
1163
+ const args = this._transformArguments(
1164
+ directive.arguments || [],
1165
+ directiveDef.args.map(item => {
1166
+ return {
1167
+ name: item.name,
1168
+ type: item.type,
1169
+ defaultValue: item.defaultValue,
1170
+ };
1171
+ }),
1172
+ null,
1173
+ name,
1174
+ );
1175
+ return {
1176
+ kind: 'Directive',
1177
+ loc: buildLocation(directive.loc),
1178
+ name,
1179
+ args,
1180
+ };
1181
+ });
1182
+ }
1183
+
1184
+ _transformArguments(
1185
+ args: $ReadOnlyArray<ArgumentNode>,
1186
+ argumentDefinitions: $ReadOnlyArray<FieldArgument>,
1187
+ field?: ?FieldID,
1188
+ directiveName?: ?string,
1189
+ ): $ReadOnlyArray<Argument> {
1190
+ return args.map(arg => {
1191
+ const argName = getName(arg);
1192
+ const argDef = argumentDefinitions.find(def => def.name === argName);
1193
+ if (argDef == null) {
1194
+ const message =
1195
+ `Unknown argument '${argName}'` +
1196
+ (field
1197
+ ? ` on field '${this._schema.getFieldName(field)}'` +
1198
+ ` of type '${this._schema.getTypeString(
1199
+ this._schema.getFieldParentType(field),
1200
+ )}'.`
1201
+ : directiveName != null
1202
+ ? ` on directive '@${directiveName}'.`
1203
+ : '.');
1204
+
1205
+ throw createUserError(message, null, [arg]);
1206
+ }
1207
+
1208
+ const value = this._transformValue(arg.value, argDef.type);
1209
+ return {
1210
+ kind: 'Argument',
1211
+ loc: buildLocation(arg.loc),
1212
+ name: argName,
1213
+ value,
1214
+ type: argDef.type,
1215
+ };
1216
+ });
1217
+ }
1218
+
1219
+ _splitConditions(
1220
+ mixedDirectives: $ReadOnlyArray<Directive>,
1221
+ ): [$ReadOnlyArray<Condition>, $ReadOnlyArray<Directive>] {
1222
+ const [conditionDirectives, otherDirectives] = partitionArray(
1223
+ mixedDirectives,
1224
+ directive => directive.name === INCLUDE || directive.name === SKIP,
1225
+ );
1226
+ const conditions = conditionDirectives.map(directive => {
1227
+ const passingValue = directive.name === INCLUDE;
1228
+ const arg = directive.args[0];
1229
+ if (arg == null || arg.name !== IF) {
1230
+ throw createUserError(
1231
+ `Expected an 'if' argument to @${directive.name}.`,
1232
+ [directive.loc],
1233
+ );
1234
+ }
1235
+ if (!(arg.value.kind === 'Variable' || arg.value.kind === 'Literal')) {
1236
+ throw createUserError(
1237
+ `Expected the 'if' argument to @${directive.name} to be a variable or literal.`,
1238
+ [directive.loc],
1239
+ );
1240
+ }
1241
+ return {
1242
+ kind: 'Condition',
1243
+ condition: arg.value,
1244
+ loc: directive.loc,
1245
+ passingValue,
1246
+ selections: [],
1247
+ };
1248
+ });
1249
+ const sortedConditions = conditions.sort((a, b) => {
1250
+ if (a.condition.kind === 'Variable' && b.condition.kind === 'Variable') {
1251
+ return a.condition.variableName < b.condition.variableName
1252
+ ? -1
1253
+ : a.condition.variableName > b.condition.variableName
1254
+ ? 1
1255
+ : 0;
1256
+ } else {
1257
+ // sort literals earlier, variables later
1258
+ return a.condition.kind === 'Variable'
1259
+ ? 1
1260
+ : b.condition.kind === 'Variable'
1261
+ ? -1
1262
+ : 0;
1263
+ }
1264
+ });
1265
+ return [sortedConditions, otherDirectives];
1266
+ }
1267
+
1268
+ _transformVariable(ast: VariableNode, usedAsType: ?InputTypeID): Variable {
1269
+ const variableName = getName(ast);
1270
+ this._recordAndVerifyVariableReference(ast, variableName, usedAsType);
1271
+ return {
1272
+ kind: 'Variable',
1273
+ loc: buildLocation(ast.loc),
1274
+ variableName,
1275
+ type: usedAsType,
1276
+ };
1277
+ }
1278
+
1279
+ _transformValue(ast: ValueNode, type: InputTypeID): ArgumentValue {
1280
+ return transformValue(
1281
+ this._schema,
1282
+ ast,
1283
+ type,
1284
+ (variableAst, variableType) =>
1285
+ this._transformVariable(variableAst, variableType),
1286
+ );
1287
+ }
1288
+ }
1289
+
1290
+ /**
1291
+ * Transforms and validates argument values according to the expected
1292
+ * type.
1293
+ */
1294
+ function transformValue(
1295
+ schema: Schema,
1296
+ ast: ValueNode,
1297
+ type: InputTypeID,
1298
+ transformVariable: (
1299
+ variableAst: VariableNode,
1300
+ variableType: InputTypeID,
1301
+ ) => ArgumentValue,
1302
+ options: {|+nonStrictEnums: boolean|} = {nonStrictEnums: false},
1303
+ ): ArgumentValue {
1304
+ if (ast.kind === 'Variable') {
1305
+ // Special case variables since there is no value to parse
1306
+ return transformVariable(ast, type);
1307
+ } else if (ast.kind === 'NullValue') {
1308
+ // Special case null literals since there is no value to parse
1309
+ if (schema.isNonNull(type)) {
1310
+ throw createUserError(
1311
+ `Expected a value matching type '${String(type)}'.`,
1312
+ null,
1313
+ [ast],
1314
+ );
1315
+ }
1316
+ return {
1317
+ kind: 'Literal',
1318
+ loc: buildLocation(ast.loc),
1319
+ value: null,
1320
+ };
1321
+ } else {
1322
+ return transformNonNullLiteral(
1323
+ schema,
1324
+ ast,
1325
+ type,
1326
+ transformVariable,
1327
+ options,
1328
+ );
1329
+ }
1330
+ }
1331
+
1332
+ /**
1333
+ * Transforms and validates non-null literal (non-variable) values
1334
+ * according to the expected type.
1335
+ */
1336
+ function transformNonNullLiteral(
1337
+ schema: Schema,
1338
+ ast: NonNullLiteralValueNode,
1339
+ type: InputTypeID,
1340
+ transformVariable: (
1341
+ variableAst: VariableNode,
1342
+ variableType: InputTypeID,
1343
+ ) => ArgumentValue,
1344
+ options: {|+nonStrictEnums: boolean|},
1345
+ ): ArgumentValue {
1346
+ // Transform the value based on the type without a non-null wrapper.
1347
+ // Note that error messages should still use the original `type`
1348
+ // since that accurately describes to the user what the expected
1349
+ // type is (using nullableType would suggest that `null` is legal
1350
+ // even when it may not be, for example).
1351
+ const nullableType = schema.getNullableType(type);
1352
+ if (schema.isList(nullableType)) {
1353
+ if (ast.kind !== 'ListValue') {
1354
+ // Parse singular (non-list) values flowing into a list type
1355
+ // as scalars, ie without wrapping them in an array.
1356
+ if (!schema.isInputType(schema.getListItemType(nullableType))) {
1357
+ throw createUserError(
1358
+ `Expected type ${schema.getTypeString(
1359
+ nullableType,
1360
+ )} to be an input type.`,
1361
+ null,
1362
+ [ast],
1363
+ );
1364
+ }
1365
+ return transformValue(
1366
+ schema,
1367
+ ast,
1368
+ schema.assertInputType(schema.getListItemType(nullableType)),
1369
+ transformVariable,
1370
+ options,
1371
+ );
1372
+ }
1373
+ const itemType = schema.assertInputType(
1374
+ schema.getListItemType(nullableType),
1375
+ );
1376
+ const literalList = [];
1377
+ const items = [];
1378
+ let areAllItemsScalar = true;
1379
+ ast.values.forEach(item => {
1380
+ const itemValue = transformValue(
1381
+ schema,
1382
+ item,
1383
+ itemType,
1384
+ transformVariable,
1385
+ options,
1386
+ );
1387
+ if (itemValue.kind === 'Literal') {
1388
+ literalList.push(itemValue.value);
1389
+ }
1390
+ items.push(itemValue);
1391
+ areAllItemsScalar = areAllItemsScalar && itemValue.kind === 'Literal';
1392
+ });
1393
+ if (areAllItemsScalar) {
1394
+ return {
1395
+ kind: 'Literal',
1396
+ loc: buildLocation(ast.loc),
1397
+ value: literalList,
1398
+ };
1399
+ } else {
1400
+ return {
1401
+ kind: 'ListValue',
1402
+ loc: buildLocation(ast.loc),
1403
+ items,
1404
+ };
1405
+ }
1406
+ } else if (schema.isInputObject(nullableType)) {
1407
+ if (ast.kind !== 'ObjectValue') {
1408
+ throw createUserError(
1409
+ `Expected a value matching type '${schema.getTypeString(type)}'.`,
1410
+ null,
1411
+ [ast],
1412
+ );
1413
+ }
1414
+ const literalObject = {};
1415
+ const fields = [];
1416
+ let areAllFieldsScalar = true;
1417
+ const inputType = schema.assertInputObjectType(nullableType);
1418
+ const requiredFieldNames = new Set(
1419
+ schema
1420
+ .getFields(inputType)
1421
+ .filter(field => {
1422
+ return schema.isNonNull(schema.getFieldType(field));
1423
+ })
1424
+ .map(field => schema.getFieldName(field)),
1425
+ );
1426
+
1427
+ const seenFields = new Map();
1428
+ ast.fields.forEach(field => {
1429
+ const fieldName = getName(field);
1430
+ const seenField = seenFields.get(fieldName);
1431
+ if (seenField) {
1432
+ throw createUserError(
1433
+ `Duplicated field name '${fieldName}' in the input object.`,
1434
+ null,
1435
+ [field, seenField],
1436
+ );
1437
+ }
1438
+ const fieldID = schema.getFieldByName(inputType, fieldName);
1439
+ if (!fieldID) {
1440
+ throw createUserError(
1441
+ `Unknown field '${fieldName}' on type '${schema.getTypeString(
1442
+ inputType,
1443
+ )}'.`,
1444
+ null,
1445
+ [field],
1446
+ );
1447
+ }
1448
+ const fieldConfig = schema.getFieldConfig(fieldID);
1449
+ const fieldType = schema.assertInputType(fieldConfig.type);
1450
+ const fieldValue = transformValue(
1451
+ schema,
1452
+ field.value,
1453
+ fieldType,
1454
+ transformVariable,
1455
+ options,
1456
+ );
1457
+ if (fieldValue.kind === 'Literal') {
1458
+ literalObject[field.name.value] = fieldValue.value;
1459
+ }
1460
+ fields.push({
1461
+ kind: 'ObjectFieldValue',
1462
+ loc: buildLocation(field.loc),
1463
+ name: fieldName,
1464
+ value: fieldValue,
1465
+ });
1466
+ seenFields.set(fieldName, field);
1467
+ requiredFieldNames.delete(fieldName);
1468
+ areAllFieldsScalar = areAllFieldsScalar && fieldValue.kind === 'Literal';
1469
+ });
1470
+ if (requiredFieldNames.size > 0) {
1471
+ const requiredFieldStr = Array.from(requiredFieldNames)
1472
+ .map(item => `'${item}'`)
1473
+ .join(', ');
1474
+ throw createUserError(
1475
+ `Missing non-optional field${
1476
+ requiredFieldNames.size > 1 ? 's:' : ''
1477
+ } ${requiredFieldStr} for input type '${schema.getTypeString(
1478
+ inputType,
1479
+ )}'.`,
1480
+ null,
1481
+ [ast],
1482
+ );
1483
+ }
1484
+ if (areAllFieldsScalar) {
1485
+ return {
1486
+ kind: 'Literal',
1487
+ loc: buildLocation(ast.loc),
1488
+ value: literalObject,
1489
+ };
1490
+ } else {
1491
+ return {
1492
+ kind: 'ObjectValue',
1493
+ loc: buildLocation(ast.loc),
1494
+ fields,
1495
+ };
1496
+ }
1497
+ } else if (schema.isId(nullableType)) {
1498
+ // GraphQLID's parseLiteral() always returns the string value. However
1499
+ // the int/string distinction may be important at runtime, so this
1500
+ // transform parses int/string literals into the corresponding JS types.
1501
+ if (ast.kind === 'IntValue') {
1502
+ return {
1503
+ kind: 'Literal',
1504
+ loc: buildLocation(ast.loc),
1505
+ value: parseInt(ast.value, 10),
1506
+ };
1507
+ } else if (ast.kind === 'StringValue') {
1508
+ return {
1509
+ kind: 'Literal',
1510
+ loc: buildLocation(ast.loc),
1511
+ value: ast.value,
1512
+ };
1513
+ } else {
1514
+ throw createUserError(
1515
+ `Invalid value, expected a value matching type '${schema.getTypeString(
1516
+ type,
1517
+ )}'.`,
1518
+ null,
1519
+ [ast],
1520
+ );
1521
+ }
1522
+ } else if (schema.isEnum(nullableType)) {
1523
+ const enumType = schema.assertEnumType(nullableType);
1524
+ const value = schema.parseLiteral(enumType, ast);
1525
+ if (value == null) {
1526
+ if (options.nonStrictEnums) {
1527
+ if (ast.kind === 'StringValue' || ast.kind === 'EnumValue') {
1528
+ const alternateValue =
1529
+ schema.parseValue(enumType, ast.value.toUpperCase()) ??
1530
+ schema.parseValue(enumType, ast.value.toLowerCase());
1531
+ if (alternateValue != null) {
1532
+ // Use the original raw value
1533
+ return {
1534
+ kind: 'Literal',
1535
+ loc: buildLocation(ast.loc),
1536
+ value: ast.value,
1537
+ };
1538
+ }
1539
+ }
1540
+ }
1541
+ const suggestions = schema.getEnumValues(enumType);
1542
+
1543
+ // parseLiteral() should return a non-null JavaScript value
1544
+ // if the ast value is valid for the type.
1545
+ throw createUserError(
1546
+ `Expected a value matching type '${schema.getTypeString(
1547
+ type,
1548
+ )}'. Possible values: ${orList(suggestions)}?'`,
1549
+ null,
1550
+ [ast],
1551
+ );
1552
+ }
1553
+ return {
1554
+ kind: 'Literal',
1555
+ loc: buildLocation(ast.loc),
1556
+ value,
1557
+ };
1558
+ } else if (schema.isScalar(nullableType)) {
1559
+ const value = schema.parseLiteral(
1560
+ schema.assertScalarType(nullableType),
1561
+ ast,
1562
+ );
1563
+ if (value == null) {
1564
+ // parseLiteral() should return a non-null JavaScript value
1565
+ // if the ast value is valid for the type.
1566
+ throw createUserError(
1567
+ `Expected a value matching type '${schema.getTypeString(type)}'.`,
1568
+ null,
1569
+ [ast],
1570
+ );
1571
+ }
1572
+ return {
1573
+ kind: 'Literal',
1574
+ loc: buildLocation(ast.loc),
1575
+ value,
1576
+ };
1577
+ } else {
1578
+ throw createCompilerError(
1579
+ `Unsupported type '${schema.getTypeString(
1580
+ type,
1581
+ )}' for input value, expected a GraphQLList, ` +
1582
+ 'GraphQLInputObjectType, GraphQLEnumType, or GraphQLScalarType.',
1583
+ null,
1584
+ [ast],
1585
+ );
1586
+ }
1587
+ }
1588
+
1589
+ /**
1590
+ * @private
1591
+ */
1592
+ function transformLiteralValue(ast: ValueNode, context: ASTNode): mixed {
1593
+ switch (ast.kind) {
1594
+ case 'IntValue':
1595
+ return parseInt(ast.value, 10);
1596
+ case 'FloatValue':
1597
+ return parseFloat(ast.value);
1598
+ case 'StringValue':
1599
+ return ast.value;
1600
+ case 'BooleanValue':
1601
+ // Note: duplicated because Flow does not understand fall-through cases
1602
+ return ast.value;
1603
+ case 'EnumValue':
1604
+ // Note: duplicated because Flow does not understand fall-through cases
1605
+ return ast.value;
1606
+ case 'ListValue':
1607
+ return ast.values.map(item => transformLiteralValue(item, context));
1608
+ case 'NullValue':
1609
+ return null;
1610
+ case 'ObjectValue': {
1611
+ const objectValue = {};
1612
+ ast.fields.forEach(field => {
1613
+ const fieldName = getName(field);
1614
+ const value = transformLiteralValue(field.value, context);
1615
+ objectValue[fieldName] = value;
1616
+ });
1617
+ return objectValue;
1618
+ }
1619
+ case 'Variable':
1620
+ throw createUserError(
1621
+ 'Unexpected variable where a literal (static) value is required.',
1622
+ null,
1623
+ [ast, context],
1624
+ );
1625
+ default:
1626
+ (ast.kind: empty);
1627
+ throw createCompilerError(`Unknown ast kind '${ast.kind}'.`, [ast]);
1628
+ }
1629
+ }
1630
+
1631
+ /**
1632
+ * @private
1633
+ */
1634
+ function buildArgumentDefinitions(
1635
+ variables: VariableDefinitions,
1636
+ ): $ReadOnlyArray<LocalArgumentDefinition> {
1637
+ return Array.from(variables.values(), ({ast, name, defaultValue, type}) => {
1638
+ return {
1639
+ kind: 'LocalArgumentDefinition',
1640
+ loc: buildLocation(ast.loc),
1641
+ name,
1642
+ type,
1643
+ defaultValue,
1644
+ };
1645
+ });
1646
+ }
1647
+
1648
+ /**
1649
+ * @private
1650
+ */
1651
+ function buildLocation(loc: ?ASTLocation): Location {
1652
+ if (loc == null) {
1653
+ return {kind: 'Unknown'};
1654
+ }
1655
+ return {
1656
+ kind: 'Source',
1657
+ start: loc.start,
1658
+ end: loc.end,
1659
+ source: loc.source,
1660
+ };
1661
+ }
1662
+
1663
+ /**
1664
+ * @private
1665
+ */
1666
+ function applyConditions(
1667
+ conditions: $ReadOnlyArray<Condition>,
1668
+ selections: $ReadOnlyArray<Selection>,
1669
+ ): $ReadOnlyArray<Condition | Selection> {
1670
+ let nextSelections = selections;
1671
+ conditions.forEach(condition => {
1672
+ nextSelections = [
1673
+ {
1674
+ ...condition,
1675
+ selections: nextSelections,
1676
+ },
1677
+ ];
1678
+ });
1679
+ return nextSelections;
1680
+ }
1681
+
1682
+ /**
1683
+ * @private
1684
+ */
1685
+ function getName(ast): string {
1686
+ const name = ast.name?.value;
1687
+ if (typeof name !== 'string') {
1688
+ throw createCompilerError("Expected ast node to have a 'name'.", null, [
1689
+ ast,
1690
+ ]);
1691
+ }
1692
+ return name;
1693
+ }
1694
+
1695
+ function getTypeName(ast: ?TypeNode): string {
1696
+ return ast ? print(ast) : 'Undefined Type Name';
1697
+ }
1698
+
1699
+ /**
1700
+ * @private
1701
+ */
1702
+ function getFragmentType(ast: ASTDefinitionNode): TypeNode {
1703
+ if (ast.kind === 'FragmentDefinition') {
1704
+ return ast.typeCondition;
1705
+ }
1706
+ throw createCompilerError(
1707
+ 'Expected ast node to be a FragmentDefinition node.',
1708
+ null,
1709
+ [ast],
1710
+ );
1711
+ }
1712
+
1713
+ function checkFragmentSpreadTypeCompatibility(
1714
+ schema: Schema,
1715
+ fragmentType: CompositeTypeID,
1716
+ parentType: TypeID,
1717
+ fragmentName: ?string,
1718
+ fragmentTypeAST: ?TypeNode | ?FragmentSpreadNode,
1719
+ parentTypeAST: ?TypeNode,
1720
+ ) {
1721
+ if (
1722
+ !schema.doTypesOverlap(fragmentType, schema.assertCompositeType(parentType))
1723
+ ) {
1724
+ const nodes = [];
1725
+ if (parentTypeAST) {
1726
+ nodes.push(parentTypeAST);
1727
+ }
1728
+ if (fragmentTypeAST) {
1729
+ nodes.push(fragmentTypeAST);
1730
+ }
1731
+
1732
+ const possibleConcreteTypes = schema.isAbstractType(parentType)
1733
+ ? Array.from(
1734
+ schema.getPossibleTypes(schema.assertAbstractType(parentType)),
1735
+ )
1736
+ : [];
1737
+ let suggestedTypesMessage = '';
1738
+ if (possibleConcreteTypes.length !== 0) {
1739
+ suggestedTypesMessage = ` Possible concrete types include ${possibleConcreteTypes
1740
+ .sort()
1741
+ .slice(0, 3)
1742
+ .map(type => `'${schema.getTypeString(type)}'`)
1743
+ .join(', ')}, etc.`;
1744
+ }
1745
+
1746
+ throw createUserError(
1747
+ (fragmentName != null
1748
+ ? `Fragment '${fragmentName}' cannot be spread here as objects of `
1749
+ : 'Fragment cannot be spread here as objects of ') +
1750
+ `type '${schema.getTypeString(parentType)}' ` +
1751
+ `can never be of type '${schema.getTypeString(fragmentType)}'.` +
1752
+ suggestedTypesMessage,
1753
+ null,
1754
+ nodes,
1755
+ );
1756
+ }
1757
+ }
1758
+
1759
+ module.exports = {parse, transform};