UNPKG

9.92 kBJavaScriptView Raw
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 * strict-local
8 * @format
9 */
10'use strict';
11
12var _objectSpread2 = require("@babel/runtime/helpers/interopRequireDefault")(require("@babel/runtime/helpers/objectSpread"));
13
14/**
15 * Returns a transformed version of the input context where each document's
16 * argument definitions are updated to accurately describe the root variables
17 * used (or reachable) from that document:
18 * - Fragment argument definitions are updated to include local argument
19 * definitions and any root variables that are referenced
20 * by the fragment (or any fragments it transitively spreads).
21 * - Root argument definitions are updated to reflect the variables
22 * referenced locally and all root variables referenced by any
23 * fragments it (transitively) spreads.
24 */
25function inferRootArgumentDefinitions(context) {
26 // This transform does two main tasks:
27 // - Determine the set of root variables referenced locally in each
28 // fragment. Note that RootArgumentDefinitions in the fragment's
29 // argumentDefinitions can contain spurious entries for legacy
30 // reasons. Instead of using those the fragment is traversed
31 // to reanalyze variable usage.
32 // - Determine the set of root variables that are transitively referenced
33 // by each fragment, ie the union of all root variables used in the
34 // fragment and any fragments it transitively spreads.
35 // Cache fragments as they are transformed to avoid duplicate processing.
36 // Because @argument values don't matter (only variable names/types),
37 // each reachable fragment only has to be checked once.
38 var transformed = new Map();
39 var nextContext = new (require("./GraphQLCompilerContext"))(context.serverSchema, context.clientSchema);
40 return nextContext.addAll(Array.from(context.documents(), function (node) {
41 switch (node.kind) {
42 case 'Fragment':
43 {
44 var argumentDefinitions = transformFragmentArguments(context, transformed, node);
45 return (0, _objectSpread2["default"])({}, node, {
46 argumentDefinitions: Array.from(argumentDefinitions.values())
47 });
48 }
49
50 case 'Root':
51 {
52 return transformRoot(context, transformed, node);
53 }
54
55 case 'SplitOperation':
56 {
57 return node;
58 }
59
60 default:
61 {
62 node;
63 throw require("./RelayCompilerError").createCompilerError("inferRootArgumentDefinitions: Unsupported kind '".concat(node.kind, "'."));
64 }
65 }
66 }));
67}
68
69function transformRoot(context, transformed, root) {
70 // Ignore argument definitions, determine what root variables are
71 // transitively referenced
72 var argumentDefinitions = new Map();
73 var localArgumentDefinitions = new Map();
74 var _iteratorNormalCompletion = true;
75 var _didIteratorError = false;
76 var _iteratorError = undefined;
77
78 try {
79 for (var _iterator = root.argumentDefinitions.entries()[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
80 var _step$value = _step.value,
81 name = _step$value[0],
82 argDef = _step$value[1];
83
84 if (argDef.kind === 'LocalArgumentDefinition') {
85 localArgumentDefinitions.set(name, argDef);
86 }
87 }
88 } catch (err) {
89 _didIteratorError = true;
90 _iteratorError = err;
91 } finally {
92 try {
93 if (!_iteratorNormalCompletion && _iterator["return"] != null) {
94 _iterator["return"]();
95 }
96 } finally {
97 if (_didIteratorError) {
98 throw _iteratorError;
99 }
100 }
101 }
102
103 visit(context, transformed, argumentDefinitions, root);
104 return (0, _objectSpread2["default"])({}, root, {
105 argumentDefinitions: Array.from(argumentDefinitions.values(), function (argDef) {
106 var _ref, _ref2;
107
108 if (argDef.kind !== 'RootArgumentDefinition') {
109 throw require("./RelayCompilerError").createCompilerError("inferRootArgumentDefinitions: Expected inferred variable '$".concat(argDef.name, "' to be a root variables."), [argDef.loc]);
110 }
111
112 var localDefinition = localArgumentDefinitions.get(argDef.name);
113 return {
114 defaultValue: (_ref = localDefinition === null || localDefinition === void 0 ? void 0 : localDefinition.defaultValue) !== null && _ref !== void 0 ? _ref : null,
115 kind: 'LocalArgumentDefinition',
116 loc: argDef.loc,
117 metadata: null,
118 name: argDef.name,
119 type: (_ref2 = localDefinition === null || localDefinition === void 0 ? void 0 : localDefinition.type) !== null && _ref2 !== void 0 ? _ref2 : argDef.type
120 };
121 })
122 });
123}
124
125function transformFragmentArguments(context, transformed, fragment) {
126 var name = fragment.name;
127 var transformedArguments = transformed.get(name);
128
129 if (transformedArguments != null) {
130 return transformedArguments;
131 } // Start with only the explicitly defined local arguments, recover the
132 // correct set of root variables excluding invalid @arguments values.
133
134
135 var argumentDefinitions = new Map();
136 fragment.argumentDefinitions.forEach(function (argDef) {
137 if (argDef.kind === 'LocalArgumentDefinition') {
138 argumentDefinitions.set(argDef.name, argDef);
139 }
140 }); // Break cycles by initially caching a version that only has local
141 // arguments. If the current fragment is reached again, it won't have
142 // any root variables to add to its parents. The traversal below will
143 // find any root variables and update the cached version of the
144 // fragment.
145
146 transformed.set(name, argumentDefinitions);
147 visit(context, transformed, argumentDefinitions, fragment);
148 transformed.set(name, argumentDefinitions);
149 return argumentDefinitions;
150}
151
152function visit(context, transformed, argumentDefinitions, node) {
153 require("./GraphQLIRVisitor").visit(node, {
154 FragmentSpread: function FragmentSpread(fragmentSpread) {
155 var fragment;
156
157 try {
158 fragment = context.getFragment(fragmentSpread.name);
159 } catch (_unused) {
160 // Handle cases where a compat fragment references a classic fragment
161 // that is not accessible to Relay compiler
162 // TODO: disallow unknown fragment references
163 // throw createCompilerError(
164 // `Document '${node.name}' referenced unknown fragment '${
165 // fragmentSpread.name
166 // }'.`,
167 // [fragmentSpread.loc],
168 // );
169 return false;
170 }
171
172 var referencedFragmentArguments = transformFragmentArguments(context, transformed, fragment); // Detect root variables being passed as the value of @arguments;
173 // recover the expected type from the corresponding argument definitions.
174
175 fragmentSpread.args.forEach(function (arg) {
176 var argDef = referencedFragmentArguments.get(arg.name);
177
178 if (argDef != null && arg.value.kind === 'Variable' && !argumentDefinitions.has(arg.value.variableName)) {
179 argumentDefinitions.set(arg.value.variableName, {
180 kind: 'RootArgumentDefinition',
181 loc: {
182 kind: 'Derived',
183 source: arg.loc
184 },
185 metadata: null,
186 name: arg.value.variableName,
187 type: argDef.type
188 });
189 }
190 }); // Merge any root variables referenced by the spread fragment
191 // into this (parent) fragment's arguments.
192
193 var _iteratorNormalCompletion2 = true;
194 var _didIteratorError2 = false;
195 var _iteratorError2 = undefined;
196
197 try {
198 for (var _iterator2 = referencedFragmentArguments.values()[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
199 var argDef = _step2.value;
200
201 if (argDef.kind === 'RootArgumentDefinition' && !argumentDefinitions.has(argDef.name)) {
202 argumentDefinitions.set(argDef.name, argDef);
203 }
204 }
205 } catch (err) {
206 _didIteratorError2 = true;
207 _iteratorError2 = err;
208 } finally {
209 try {
210 if (!_iteratorNormalCompletion2 && _iterator2["return"] != null) {
211 _iterator2["return"]();
212 }
213 } finally {
214 if (_didIteratorError2) {
215 throw _iteratorError2;
216 }
217 }
218 }
219
220 return false;
221 },
222 Argument: function Argument(argument) {
223 if (argument.value.kind !== 'Variable') {
224 return false;
225 }
226
227 var variable = argument.value;
228
229 if (argument.type == null && variable.type == null) {
230 return;
231 }
232
233 if (!argumentDefinitions.has(variable.variableName)) {
234 // root variable
235 argumentDefinitions.set(variable.variableName, {
236 kind: 'RootArgumentDefinition',
237 loc: {
238 kind: 'Derived',
239 source: argument.loc
240 },
241 metadata: null,
242 name: variable.variableName,
243 type: variable.type || argument.type
244 });
245 }
246
247 return false;
248 },
249 Condition: function Condition(condition) {
250 var _variable$type;
251
252 if (condition.condition.kind !== 'Variable') {
253 return;
254 }
255
256 var variable = condition.condition;
257 var type = (_variable$type = variable.type) !== null && _variable$type !== void 0 ? _variable$type : new (require("graphql").GraphQLNonNull)(require("graphql").GraphQLBoolean);
258
259 if (!argumentDefinitions.has(variable.variableName)) {
260 // root variable
261 argumentDefinitions.set(variable.variableName, {
262 kind: 'RootArgumentDefinition',
263 loc: {
264 kind: 'Derived',
265 source: condition.loc
266 },
267 metadata: null,
268 name: variable.variableName,
269 type: type
270 });
271 }
272 }
273 });
274}
275
276module.exports = inferRootArgumentDefinitions;
\No newline at end of file