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,100 @@
1
+ /**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @format
8
+ * @flow strict-local
9
+ */
10
+
11
+ // flowlint ambiguous-object-type:error
12
+
13
+ 'use strict';
14
+
15
+ const JSModuleParser = require('../core/JSModuleParser');
16
+
17
+ const invariant = require('invariant');
18
+
19
+ const {parse, print} = require('graphql');
20
+
21
+ import type {WatchmanFile} from './types';
22
+ import type {ASTNode, ExecutableDefinitionNode} from 'graphql';
23
+
24
+ export type ASTRecord<T: ASTNode> = {|
25
+ +ast: T,
26
+ +text: string,
27
+ |};
28
+
29
+ export type ExtractFn<T> = (
30
+ baseDir: string,
31
+ file: WatchmanFile,
32
+ ) => ?{|
33
+ +nodes: $ReadOnlyArray<ASTRecord<T>>,
34
+ +sources: $ReadOnlyArray<string>,
35
+ |};
36
+
37
+ function extractFromJS(
38
+ baseDir: string,
39
+ file: WatchmanFile,
40
+ ): ?{|
41
+ +nodes: $ReadOnlyArray<ASTRecord<ExecutableDefinitionNode>>,
42
+ +sources: $ReadOnlyArray<string>,
43
+ |} {
44
+ if (!file.exists) {
45
+ return null;
46
+ }
47
+ const f = {
48
+ relPath: file.name,
49
+ exists: true,
50
+ hash: file['content.sha1hex'],
51
+ };
52
+ const fileFilter = JSModuleParser.getFileFilter(baseDir);
53
+ if (!fileFilter(f)) {
54
+ return null;
55
+ }
56
+ const result = JSModuleParser.parseFileWithSources(baseDir, f);
57
+ if (result == null || result.document.definitions.length === 0) {
58
+ return null;
59
+ }
60
+
61
+ const {document: doc, sources} = result;
62
+
63
+ const nodes = doc.definitions.map(def => {
64
+ if (
65
+ def.kind === 'FragmentDefinition' ||
66
+ def.kind === 'OperationDefinition'
67
+ ) {
68
+ return toASTRecord(def);
69
+ }
70
+ throw new Error(`Unexpected definition kind: ${def.kind}`);
71
+ });
72
+ return {
73
+ nodes,
74
+ sources,
75
+ };
76
+ }
77
+
78
+ function toASTRecord<T: ASTNode>(node: T): ASTRecord<T> {
79
+ return {
80
+ ast: node,
81
+ text: print(node),
82
+ };
83
+ }
84
+
85
+ function parseExecutableNode(text: string): ExecutableDefinitionNode {
86
+ const nodes = parse(text).definitions;
87
+ invariant(nodes.length === 1, 'expected exactly 1 definition');
88
+ const node = nodes[0];
89
+ invariant(
90
+ node.kind === 'OperationDefinition' || node.kind === 'FragmentDefinition',
91
+ 'expected an ExecutableDefinitionNode',
92
+ );
93
+ return node;
94
+ }
95
+
96
+ module.exports = {
97
+ parseExecutableNode,
98
+ toASTRecord,
99
+ extractFromJS,
100
+ };
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @format
8
+ * @flow strict-local
9
+ */
10
+
11
+ // flowlint ambiguous-object-type:error
12
+
13
+ 'use strict';
14
+
15
+ const {getName} = require('./GraphQLASTUtils');
16
+
17
+ import type {SourceChanges} from './Sources';
18
+ import type {StrictMap} from './StrictMap';
19
+ import type {ExecutableDefinitionNode} from 'graphql';
20
+
21
+ function getChangedNodeNames<
22
+ TProjectState: {
23
+ +initialDirty: Set<string>,
24
+ +changes: SourceChanges<ExecutableDefinitionNode>,
25
+ ...
26
+ },
27
+ >(
28
+ projectStates: StrictMap<string, TProjectState>,
29
+ projects: $ReadOnlyArray<string>,
30
+ ): Set<string> {
31
+ const changedNames = new Set();
32
+ for (const projectType of projects) {
33
+ const subConfig = projectStates.get(projectType);
34
+ for (const name of subConfig.initialDirty) {
35
+ changedNames.add(name);
36
+ }
37
+ for (const {ast} of subConfig.changes.added) {
38
+ changedNames.add(getName(ast));
39
+ }
40
+
41
+ for (const {ast} of subConfig.changes.removed) {
42
+ changedNames.add(getName(ast));
43
+ }
44
+ }
45
+ return changedNames;
46
+ }
47
+
48
+ module.exports = getChangedNodeNames;
@@ -0,0 +1,36 @@
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
8
+ * @format
9
+ */
10
+
11
+ // flowlint ambiguous-object-type:error
12
+
13
+ 'use strict';
14
+
15
+ const {create} = require('../core/Schema');
16
+
17
+ import type {Schema} from '../core/Schema';
18
+ import type {Source, DocumentNode} from 'graphql';
19
+
20
+ const schemaCache: Map<Source, Schema> = new Map();
21
+
22
+ function getSchemaInstance(
23
+ getSchemaSource: () => Source,
24
+ getSchemaExtensions: () => $ReadOnlyArray<DocumentNode>,
25
+ schemaExtensions: $ReadOnlyArray<string>,
26
+ ): Schema {
27
+ const source = getSchemaSource();
28
+ let schema = schemaCache.get(source);
29
+ if (schema == null) {
30
+ schema = create(source, getSchemaExtensions(), schemaExtensions);
31
+ schemaCache.set(source, schema);
32
+ }
33
+ return schema;
34
+ }
35
+
36
+ module.exports = getSchemaInstance;
@@ -0,0 +1,37 @@
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
+ * @emails oncall+relay
10
+ */
11
+
12
+ // flowlint ambiguous-object-type:error
13
+
14
+ 'use strict';
15
+
16
+ export type WatchmanFile =
17
+ | {
18
+ +exists: true,
19
+ +name: string,
20
+ +'content.sha1hex': string,
21
+ ...
22
+ }
23
+ | {
24
+ +exists: false,
25
+ +name: string,
26
+ ...
27
+ };
28
+
29
+ export type SavedStateCollection = $ReadOnlyArray<{|
30
+ +file: string,
31
+ +sources: $ReadOnlyArray<string>,
32
+ |}>;
33
+
34
+ export type SavedState<TSubProjectsType> = {
35
+ [TSubProjectsType]: SavedStateCollection,
36
+ ...,
37
+ };
@@ -0,0 +1,474 @@
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
8
+ * @format
9
+ */
10
+
11
+ // flowlint ambiguous-object-type:error
12
+
13
+ 'use strict';
14
+
15
+ const IRTransformer = require('../core/IRTransformer');
16
+ const RelayCompilerScope = require('../core/RelayCompilerScope');
17
+
18
+ const getIdentifierForArgumentValue = require('../core/getIdentifierForArgumentValue');
19
+ const murmurHash = require('../util/murmurHash');
20
+
21
+ const {
22
+ createCompilerError,
23
+ createNonRecoverableUserError,
24
+ } = require('../core/CompilerError');
25
+
26
+ import type CompilerContext from '../core/CompilerContext';
27
+ import type {
28
+ Argument,
29
+ ArgumentValue,
30
+ Condition,
31
+ Directive,
32
+ Field,
33
+ Fragment,
34
+ FragmentSpread,
35
+ IR,
36
+ Node,
37
+ Selection,
38
+ } from '../core/IR';
39
+ import type {Scope} from '../core/RelayCompilerScope';
40
+
41
+ const {getFragmentScope, getRootScope} = RelayCompilerScope;
42
+
43
+ type PendingFragment =
44
+ | {|kind: 'pending'|}
45
+ | {|kind: 'resolved', value: ?Fragment|};
46
+
47
+ /**
48
+ * A transform that converts a set of documents containing fragments/fragment
49
+ * spreads *with* arguments to one where all arguments have been inlined. This
50
+ * is effectively static currying of functions. Nodes are changed as follows:
51
+ * - Fragment spreads with arguments are replaced with references to an inlined
52
+ * version of the referenced fragment.
53
+ * - Fragments with argument definitions are cloned once per unique set of
54
+ * arguments, with the name changed to original name + hash and all nested
55
+ * variable references changed to the value of that variable given its
56
+ * arguments.
57
+ * - Field & directive argument variables are replaced with the value of those
58
+ * variables in context.
59
+ * - All nodes are cloned with updated children.
60
+ *
61
+ * The transform also handles statically passing/failing Condition nodes:
62
+ * - Literal Conditions with a passing value are elided and their selections
63
+ * inlined in their parent.
64
+ * - Literal Conditions with a failing value are removed.
65
+ * - Nodes that would become empty as a result of the above are removed.
66
+ *
67
+ * Note that unreferenced fragments are not added to the output.
68
+ */
69
+ function applyFragmentArgumentTransform(
70
+ context: CompilerContext,
71
+ ): CompilerContext {
72
+ const fragments: Map<string, PendingFragment> = new Map();
73
+ let nextContext = IRTransformer.transform(context, {
74
+ Root: node => {
75
+ const scope = getRootScope(node.argumentDefinitions);
76
+ return transformNode(context, fragments, scope, node, [node]);
77
+ },
78
+ SplitOperation: node => {
79
+ return transformNode(context, fragments, {}, node, [node]);
80
+ },
81
+ // Fragments are included below where referenced.
82
+ // Unreferenced fragments are not included.
83
+ Fragment: () => null,
84
+ });
85
+
86
+ for (const pendingFragment of fragments.values()) {
87
+ if (pendingFragment.kind === 'resolved' && pendingFragment.value) {
88
+ nextContext = nextContext.add(pendingFragment.value);
89
+ }
90
+ }
91
+ return nextContext;
92
+ }
93
+
94
+ function transformNode<T: Node>(
95
+ context: CompilerContext,
96
+ fragments: Map<string, PendingFragment>,
97
+ scope: Scope,
98
+ node: T,
99
+ errorContext: $ReadOnlyArray<IR>,
100
+ ): ?T {
101
+ const selections = transformSelections(
102
+ context,
103
+ fragments,
104
+ scope,
105
+ node.selections,
106
+ errorContext,
107
+ );
108
+ if (!selections) {
109
+ return null;
110
+ }
111
+ if (node.hasOwnProperty('directives')) {
112
+ const directives = transformDirectives(
113
+ scope,
114
+ (node: $FlowIssue).directives,
115
+ errorContext,
116
+ );
117
+ // $FlowIssue: this is a valid `Node`:
118
+ return ({
119
+ ...node,
120
+ directives,
121
+ selections,
122
+ }: any);
123
+ }
124
+ return ({
125
+ ...node,
126
+ selections,
127
+ }: $FlowIssue);
128
+ }
129
+
130
+ function transformFragmentSpread(
131
+ context: CompilerContext,
132
+ fragments: Map<string, PendingFragment>,
133
+ scope: Scope,
134
+ spread: FragmentSpread,
135
+ errorContext: $ReadOnlyArray<IR>,
136
+ ): ?FragmentSpread {
137
+ const directives = transformDirectives(
138
+ scope,
139
+ spread.directives,
140
+ errorContext,
141
+ );
142
+ const appliedFragment = transformFragment(
143
+ context,
144
+ fragments,
145
+ scope,
146
+ spread,
147
+ spread.args,
148
+ [...errorContext, spread],
149
+ );
150
+ if (!appliedFragment) {
151
+ return null;
152
+ }
153
+ const transformed: FragmentSpread = {
154
+ ...spread,
155
+ kind: 'FragmentSpread',
156
+ args: [],
157
+ directives,
158
+ name: appliedFragment.name,
159
+ };
160
+ return transformed;
161
+ }
162
+
163
+ function transformField<T: Field>(
164
+ context: CompilerContext,
165
+ fragments: Map<string, PendingFragment>,
166
+ scope: Scope,
167
+ field: T,
168
+ errorContext: $ReadOnlyArray<IR>,
169
+ ): ?T {
170
+ const args = transformArguments(scope, field.args, errorContext);
171
+ const directives = transformDirectives(scope, field.directives, errorContext);
172
+ if (field.kind === 'LinkedField') {
173
+ const selections = transformSelections(
174
+ context,
175
+ fragments,
176
+ scope,
177
+ field.selections,
178
+ errorContext,
179
+ );
180
+ if (!selections) {
181
+ return null;
182
+ }
183
+ return ({
184
+ ...field,
185
+ args,
186
+ directives,
187
+ selections,
188
+ }: $FlowFixMe);
189
+ } else {
190
+ return {
191
+ ...field,
192
+ args,
193
+ directives,
194
+ };
195
+ }
196
+ }
197
+
198
+ function transformCondition(
199
+ context: CompilerContext,
200
+ fragments: Map<string, PendingFragment>,
201
+ scope: Scope,
202
+ node: Condition,
203
+ errorContext: $ReadOnlyArray<IR>,
204
+ ): ?$ReadOnlyArray<Selection> {
205
+ const condition = transformValue(scope, node.condition, errorContext);
206
+ if (!(condition.kind === 'Literal' || condition.kind === 'Variable')) {
207
+ // This transform does whole-program optimization, errors in
208
+ // a single document could break invariants and/or cause
209
+ // additional spurious errors.
210
+ throw createNonRecoverableUserError(
211
+ 'A non-scalar value was applied to an @include or @skip directive, ' +
212
+ 'the `if` argument value must be a ' +
213
+ 'variable or a literal Boolean.',
214
+ [condition.loc],
215
+ );
216
+ }
217
+ if (condition.kind === 'Literal' && condition.value !== node.passingValue) {
218
+ // Dead code, no need to traverse further.
219
+ return null;
220
+ }
221
+ const selections = transformSelections(
222
+ context,
223
+ fragments,
224
+ scope,
225
+ node.selections,
226
+ errorContext,
227
+ );
228
+ if (!selections) {
229
+ return null;
230
+ }
231
+ if (condition.kind === 'Literal' && condition.value === node.passingValue) {
232
+ // Always passes, return inlined selections
233
+ return selections;
234
+ }
235
+ return [
236
+ {
237
+ ...node,
238
+ condition,
239
+ selections,
240
+ },
241
+ ];
242
+ }
243
+
244
+ function transformSelections(
245
+ context: CompilerContext,
246
+ fragments: Map<string, PendingFragment>,
247
+ scope: Scope,
248
+ selections: $ReadOnlyArray<Selection>,
249
+ errorContext: $ReadOnlyArray<IR>,
250
+ ): ?$ReadOnlyArray<Selection> {
251
+ let nextSelections = null;
252
+ selections.forEach(selection => {
253
+ let nextSelection;
254
+ if (
255
+ selection.kind === 'ClientExtension' ||
256
+ selection.kind === 'InlineDataFragmentSpread' ||
257
+ selection.kind === 'InlineFragment' ||
258
+ selection.kind === 'ModuleImport' ||
259
+ selection.kind === 'Defer' ||
260
+ selection.kind === 'Stream'
261
+ ) {
262
+ nextSelection = transformNode(
263
+ context,
264
+ fragments,
265
+ scope,
266
+ selection,
267
+ errorContext,
268
+ );
269
+ } else if (selection.kind === 'FragmentSpread') {
270
+ nextSelection = transformFragmentSpread(
271
+ context,
272
+ fragments,
273
+ scope,
274
+ selection,
275
+ errorContext,
276
+ );
277
+ } else if (selection.kind === 'Condition') {
278
+ const conditionSelections = transformCondition(
279
+ context,
280
+ fragments,
281
+ scope,
282
+ selection,
283
+ errorContext,
284
+ );
285
+ if (conditionSelections) {
286
+ nextSelections = nextSelections || [];
287
+ nextSelections.push(...conditionSelections);
288
+ }
289
+ } else if (
290
+ selection.kind === 'LinkedField' ||
291
+ selection.kind === 'ScalarField'
292
+ ) {
293
+ nextSelection = transformField(
294
+ context,
295
+ fragments,
296
+ scope,
297
+ selection,
298
+ errorContext,
299
+ );
300
+ } else {
301
+ (selection: empty);
302
+ throw createCompilerError(
303
+ `ApplyFragmentArgumentTransform: Unsupported kind '${selection.kind}'.`,
304
+ [selection.loc],
305
+ );
306
+ }
307
+ if (nextSelection) {
308
+ nextSelections = nextSelections || [];
309
+ nextSelections.push(nextSelection);
310
+ }
311
+ });
312
+ return nextSelections;
313
+ }
314
+
315
+ function transformDirectives(
316
+ scope: Scope,
317
+ directives: $ReadOnlyArray<Directive>,
318
+ errorContext: $ReadOnlyArray<IR>,
319
+ ): $ReadOnlyArray<Directive> {
320
+ return directives.map(directive => {
321
+ const args = transformArguments(scope, directive.args, errorContext);
322
+ return {
323
+ ...directive,
324
+ args,
325
+ };
326
+ });
327
+ }
328
+
329
+ function transformArguments(
330
+ scope: Scope,
331
+ args: $ReadOnlyArray<Argument>,
332
+ errorContext: $ReadOnlyArray<IR>,
333
+ ): $ReadOnlyArray<Argument> {
334
+ return args.map(arg => {
335
+ const value = transformValue(scope, arg.value, errorContext);
336
+ return value === arg.value ? arg : {...arg, value};
337
+ });
338
+ }
339
+
340
+ function transformValue(
341
+ scope: Scope,
342
+ value: ArgumentValue,
343
+ errorContext: $ReadOnlyArray<IR>,
344
+ ): ArgumentValue {
345
+ if (value.kind === 'Variable') {
346
+ const scopeValue = scope[value.variableName];
347
+ if (scopeValue == null) {
348
+ // This transform does whole-program optimization, errors in
349
+ // a single document could break invariants and/or cause
350
+ // additional spurious errors.
351
+ throw createNonRecoverableUserError(
352
+ `Variable '$${value.variableName}' is not in scope.`,
353
+ [errorContext[0]?.loc, value.loc].filter(Boolean),
354
+ );
355
+ }
356
+ return scopeValue;
357
+ } else if (value.kind === 'ObjectValue') {
358
+ return {
359
+ ...value,
360
+ fields: value.fields.map(field => ({
361
+ ...field,
362
+ value: transformValue(scope, field.value, errorContext),
363
+ })),
364
+ };
365
+ } else if (value.kind === 'ListValue') {
366
+ return {
367
+ ...value,
368
+ items: value.items.map(item => transformValue(scope, item, errorContext)),
369
+ };
370
+ }
371
+ return value;
372
+ }
373
+
374
+ /**
375
+ * Apply arguments to a fragment, creating a new fragment (with the given name)
376
+ * with all values recursively applied.
377
+ */
378
+ function transformFragment(
379
+ context: CompilerContext,
380
+ fragments: Map<string, PendingFragment>,
381
+ parentScope: Scope,
382
+ spread: FragmentSpread,
383
+ args: $ReadOnlyArray<Argument>,
384
+ errorContext: $ReadOnlyArray<IR>,
385
+ ): ?Fragment {
386
+ const schema = context.getSchema();
387
+ const fragment = context.getFragment(spread.name, spread.loc);
388
+ const argumentsHash = hashArguments(args, parentScope, errorContext);
389
+ const fragmentName = argumentsHash
390
+ ? `${fragment.name}_${argumentsHash}`
391
+ : fragment.name;
392
+ const appliedFragment = fragments.get(fragmentName);
393
+ if (appliedFragment) {
394
+ if (appliedFragment.kind === 'resolved') {
395
+ return appliedFragment.value;
396
+ } else {
397
+ // This transform does whole-program optimization, errors in
398
+ // a single document could break invariants and/or cause
399
+ // additional spurious errors.
400
+ throw createNonRecoverableUserError(
401
+ `Found a circular reference from fragment '${fragment.name}'.`,
402
+ errorContext.map(node => node.loc),
403
+ );
404
+ }
405
+ }
406
+ const fragmentScope = getFragmentScope(
407
+ schema,
408
+ fragment.argumentDefinitions,
409
+ args,
410
+ parentScope,
411
+ spread,
412
+ );
413
+ // record that this fragment is pending to detect circular references
414
+ fragments.set(fragmentName, {kind: 'pending'});
415
+ let transformedFragment = null;
416
+ const selections = transformSelections(
417
+ context,
418
+ fragments,
419
+ fragmentScope,
420
+ fragment.selections,
421
+ errorContext,
422
+ );
423
+ if (selections) {
424
+ transformedFragment = {
425
+ ...fragment,
426
+ selections,
427
+ name: fragmentName,
428
+ argumentDefinitions: [],
429
+ };
430
+ }
431
+ fragments.set(fragmentName, {kind: 'resolved', value: transformedFragment});
432
+ return transformedFragment;
433
+ }
434
+
435
+ function hashArguments(
436
+ args: $ReadOnlyArray<Argument>,
437
+ scope: Scope,
438
+ errorContext: $ReadOnlyArray<IR>,
439
+ ): ?string {
440
+ if (!args.length) {
441
+ return null;
442
+ }
443
+ const sortedArgs = [...args].sort((a, b) => {
444
+ return a.name < b.name ? -1 : a.name > b.name ? 1 : 0;
445
+ });
446
+ const printedArgs = JSON.stringify(
447
+ sortedArgs.map(arg => {
448
+ let value;
449
+ if (arg.value.kind === 'Variable') {
450
+ value = scope[arg.value.variableName];
451
+ if (value == null) {
452
+ // This transform does whole-program optimization, errors in
453
+ // a single document could break invariants and/or cause
454
+ // additional spurious errors.
455
+ throw createNonRecoverableUserError(
456
+ `Variable '$${arg.value.variableName}' is not in scope.`,
457
+ [errorContext[0]?.loc, arg.value.loc].filter(Boolean),
458
+ );
459
+ }
460
+ } else {
461
+ value = arg.value;
462
+ }
463
+ return {
464
+ name: arg.name,
465
+ value: getIdentifierForArgumentValue(value),
466
+ };
467
+ }),
468
+ );
469
+ return murmurHash(printedArgs);
470
+ }
471
+
472
+ module.exports = {
473
+ transform: applyFragmentArgumentTransform,
474
+ };