relay-compiler 9.0.0 → 10.1.0

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