UNPKG

18.4 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 *
8 * @format
9 */
10'use strict';
11
12var _objectSpread2 = require("@babel/runtime/helpers/interopRequireDefault")(require("@babel/runtime/helpers/objectSpread"));
13
14var _toConsumableArray2 = require("@babel/runtime/helpers/interopRequireDefault")(require("@babel/runtime/helpers/toConsumableArray"));
15
16function generate(node, options) {
17 var ast = require("./GraphQLIRVisitor").visit(node, createVisitor(options));
18
19 return require("@babel/generator")["default"](ast).code;
20}
21
22function makeProp(_ref, state, unmasked, concreteType) {
23 var key = _ref.key,
24 schemaName = _ref.schemaName,
25 value = _ref.value,
26 conditional = _ref.conditional,
27 nodeType = _ref.nodeType,
28 nodeSelections = _ref.nodeSelections;
29
30 if (nodeType) {
31 value = require("./RelayFlowTypeTransformers").transformScalarType(nodeType, state, selectionsToBabel([Array.from(require("nullthrows")(nodeSelections).values())], state, unmasked));
32 }
33
34 if (schemaName === '__typename' && concreteType) {
35 value = require("@babel/types").stringLiteralTypeAnnotation(concreteType);
36 }
37
38 var typeProperty = require("./RelayFlowBabelFactories").readOnlyObjectTypeProperty(key, value);
39
40 if (conditional) {
41 typeProperty.optional = true;
42 }
43
44 return typeProperty;
45}
46
47var isTypenameSelection = function isTypenameSelection(selection) {
48 return selection.schemaName === '__typename';
49};
50
51var hasTypenameSelection = function hasTypenameSelection(selections) {
52 return selections.some(isTypenameSelection);
53};
54
55var onlySelectsTypename = function onlySelectsTypename(selections) {
56 return selections.every(isTypenameSelection);
57};
58
59function selectionsToBabel(selections, state, unmasked, refTypeName) {
60 var baseFields = new Map();
61 var byConcreteType = {};
62 flattenArray(selections).forEach(function (selection) {
63 var concreteType = selection.concreteType;
64
65 if (concreteType) {
66 var _byConcreteType$concr;
67
68 byConcreteType[concreteType] = (_byConcreteType$concr = byConcreteType[concreteType]) !== null && _byConcreteType$concr !== void 0 ? _byConcreteType$concr : [];
69 byConcreteType[concreteType].push(selection);
70 } else {
71 var previousSel = baseFields.get(selection.key);
72 baseFields.set(selection.key, previousSel ? mergeSelection(selection, previousSel) : selection);
73 }
74 });
75 var types = [];
76
77 if (Object.keys(byConcreteType).length && onlySelectsTypename(Array.from(baseFields.values())) && (hasTypenameSelection(Array.from(baseFields.values())) || Object.keys(byConcreteType).every(function (type) {
78 return hasTypenameSelection(byConcreteType[type]);
79 }))) {
80 (function () {
81 var typenameAliases = new Set();
82
83 var _loop = function _loop(concreteType) {
84 types.push(groupRefs((0, _toConsumableArray2["default"])(Array.from(baseFields.values())).concat((0, _toConsumableArray2["default"])(byConcreteType[concreteType]))).map(function (selection) {
85 if (selection.schemaName === '__typename') {
86 typenameAliases.add(selection.key);
87 }
88
89 return makeProp(selection, state, unmasked, concreteType);
90 }));
91 };
92
93 for (var concreteType in byConcreteType) {
94 _loop(concreteType);
95 } // It might be some other type then the listed concrete types. Ideally, we
96 // would set the type to diff(string, set of listed concrete types), but
97 // this doesn't exist in Flow at the time.
98
99
100 types.push(Array.from(typenameAliases).map(function (typenameAlias) {
101 var otherProp = require("./RelayFlowBabelFactories").readOnlyObjectTypeProperty(typenameAlias, require("@babel/types").stringLiteralTypeAnnotation('%other'));
102
103 otherProp.leadingComments = require("./RelayFlowBabelFactories").lineComments("This will never be '%other', but we need some", 'value in case none of the concrete values match.');
104 return otherProp;
105 }));
106 })();
107 } else {
108 var selectionMap = selectionsToMap(Array.from(baseFields.values()));
109
110 for (var concreteType in byConcreteType) {
111 selectionMap = mergeSelections(selectionMap, selectionsToMap(byConcreteType[concreteType].map(function (sel) {
112 return (0, _objectSpread2["default"])({}, sel, {
113 conditional: true
114 });
115 })));
116 }
117
118 var selectionMapValues = groupRefs(Array.from(selectionMap.values())).map(function (sel) {
119 return isTypenameSelection(sel) && sel.concreteType ? makeProp((0, _objectSpread2["default"])({}, sel, {
120 conditional: false
121 }), state, unmasked, sel.concreteType) : makeProp(sel, state, unmasked);
122 });
123 types.push(selectionMapValues);
124 }
125
126 return require("./RelayFlowBabelFactories").unionTypeAnnotation(types.map(function (props) {
127 if (refTypeName) {
128 props.push(require("./RelayFlowBabelFactories").readOnlyObjectTypeProperty('$refType', require("@babel/types").genericTypeAnnotation(require("@babel/types").identifier(refTypeName))));
129 }
130
131 return unmasked ? require("@babel/types").objectTypeAnnotation(props) : require("./RelayFlowBabelFactories").exactObjectTypeAnnotation(props);
132 }));
133}
134
135function mergeSelection(a, b) {
136 if (!a) {
137 return (0, _objectSpread2["default"])({}, b, {
138 conditional: true
139 });
140 }
141
142 return (0, _objectSpread2["default"])({}, a, {
143 nodeSelections: a.nodeSelections ? mergeSelections(a.nodeSelections, require("nullthrows")(b.nodeSelections)) : null,
144 conditional: a.conditional && b.conditional
145 });
146}
147
148function mergeSelections(a, b) {
149 var merged = new Map();
150 var _iteratorNormalCompletion = true;
151 var _didIteratorError = false;
152 var _iteratorError = undefined;
153
154 try {
155 for (var _iterator = a.entries()[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
156 var _step$value = _step.value,
157 key = _step$value[0],
158 value = _step$value[1];
159 merged.set(key, value);
160 }
161 } catch (err) {
162 _didIteratorError = true;
163 _iteratorError = err;
164 } finally {
165 try {
166 if (!_iteratorNormalCompletion && _iterator["return"] != null) {
167 _iterator["return"]();
168 }
169 } finally {
170 if (_didIteratorError) {
171 throw _iteratorError;
172 }
173 }
174 }
175
176 var _iteratorNormalCompletion2 = true;
177 var _didIteratorError2 = false;
178 var _iteratorError2 = undefined;
179
180 try {
181 for (var _iterator2 = b.entries()[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
182 var _step2$value = _step2.value,
183 key = _step2$value[0],
184 value = _step2$value[1];
185 merged.set(key, mergeSelection(a.get(key), value));
186 }
187 } catch (err) {
188 _didIteratorError2 = true;
189 _iteratorError2 = err;
190 } finally {
191 try {
192 if (!_iteratorNormalCompletion2 && _iterator2["return"] != null) {
193 _iterator2["return"]();
194 }
195 } finally {
196 if (_didIteratorError2) {
197 throw _iteratorError2;
198 }
199 }
200 }
201
202 return merged;
203}
204
205function isPlural(node) {
206 return Boolean(node.metadata && node.metadata.plural);
207}
208
209function createVisitor(options) {
210 var state = {
211 customScalars: options.customScalars,
212 enumsHasteModule: options.enumsHasteModule,
213 existingFragmentNames: options.existingFragmentNames,
214 generatedFragments: new Set(),
215 generatedInputObjectTypes: {},
216 optionalInputFields: options.optionalInputFields,
217 usedEnums: {},
218 usedFragments: new Set(),
219 useHaste: options.useHaste,
220 useSingleArtifactDirectory: options.useSingleArtifactDirectory,
221 noFutureProofEnums: options.noFutureProofEnums
222 };
223 var hasMatchField = false;
224 return {
225 leave: {
226 Root: function Root(node) {
227 var inputVariablesType = generateInputVariablesType(node, state);
228 var inputObjectTypes = generateInputObjectTypes(state);
229
230 var responseType = require("./RelayFlowBabelFactories").exportType("".concat(node.name, "Response"), selectionsToBabel(node.selections, state, false));
231
232 var operationType = require("./RelayFlowBabelFactories").exportType(node.name, require("./RelayFlowBabelFactories").exactObjectTypeAnnotation([require("@babel/types").objectTypeProperty(require("@babel/types").identifier('variables'), require("@babel/types").genericTypeAnnotation(require("@babel/types").identifier("".concat(node.name, "Variables")))), require("@babel/types").objectTypeProperty(require("@babel/types").identifier('response'), require("@babel/types").genericTypeAnnotation(require("@babel/types").identifier("".concat(node.name, "Response"))))]));
233
234 var importedTypes = [];
235
236 if (hasMatchField) {
237 importedTypes.push('MatchPointer');
238 }
239
240 return require("@babel/types").program((0, _toConsumableArray2["default"])(getFragmentImports(state)).concat((0, _toConsumableArray2["default"])(getEnumDefinitions(state)), [importedTypes.length ? require("./RelayFlowBabelFactories").importTypes(importedTypes, 'relay-runtime') : null], (0, _toConsumableArray2["default"])(inputObjectTypes), [inputVariablesType, responseType, operationType]).filter(Boolean));
241 },
242 Fragment: function Fragment(node) {
243 var selections = flattenArray(node.selections);
244 var numConecreteSelections = selections.filter(function (s) {
245 return s.concreteType;
246 }).length;
247 selections = selections.map(function (selection) {
248 if (numConecreteSelections <= 1 && isTypenameSelection(selection) && !require("./GraphQLSchemaUtils").isAbstractType(node.type)) {
249 return [(0, _objectSpread2["default"])({}, selection, {
250 concreteType: node.type.toString()
251 })];
252 }
253
254 return [selection];
255 });
256 state.generatedFragments.add(node.name);
257 var refTypeName = getRefTypeName(node.name);
258
259 var refType = require("@babel/types").declareExportDeclaration(require("@babel/types").declareOpaqueType(require("@babel/types").identifier(refTypeName), null, require("@babel/types").genericTypeAnnotation(require("@babel/types").identifier('FragmentReference'))));
260
261 var unmasked = node.metadata && node.metadata.mask === false;
262 var baseType = selectionsToBabel(selections, state, unmasked, unmasked ? undefined : refTypeName);
263 var type = isPlural(node) ? require("./RelayFlowBabelFactories").readOnlyArrayOfType(baseType) : baseType;
264 var importedTypes = ['FragmentReference'];
265
266 if (hasMatchField) {
267 importedTypes.push('MatchPointer');
268 }
269
270 return require("@babel/types").program((0, _toConsumableArray2["default"])(getFragmentImports(state)).concat((0, _toConsumableArray2["default"])(getEnumDefinitions(state)), [require("./RelayFlowBabelFactories").importTypes(importedTypes, 'relay-runtime'), refType, require("./RelayFlowBabelFactories").exportType(node.name, type)]));
271 },
272 InlineFragment: function InlineFragment(node) {
273 var typeCondition = node.typeCondition;
274 return flattenArray(node.selections).map(function (typeSelection) {
275 return require("./GraphQLSchemaUtils").isAbstractType(typeCondition) ? (0, _objectSpread2["default"])({}, typeSelection, {
276 conditional: true
277 }) : (0, _objectSpread2["default"])({}, typeSelection, {
278 concreteType: typeCondition.toString()
279 });
280 });
281 },
282 Condition: function Condition(node) {
283 return flattenArray(node.selections).map(function (selection) {
284 return (0, _objectSpread2["default"])({}, selection, {
285 conditional: true
286 });
287 });
288 },
289 ScalarField: function ScalarField(node) {
290 var _node$alias;
291
292 return [{
293 key: (_node$alias = node.alias) !== null && _node$alias !== void 0 ? _node$alias : node.name,
294 schemaName: node.name,
295 value: require("./RelayFlowTypeTransformers").transformScalarType(node.type, state)
296 }];
297 },
298 LinkedField: function LinkedField(node) {
299 var _node$alias2;
300
301 return [{
302 key: (_node$alias2 = node.alias) !== null && _node$alias2 !== void 0 ? _node$alias2 : node.name,
303 schemaName: node.name,
304 nodeType: node.type,
305 nodeSelections: selectionsToMap(flattenArray(node.selections))
306 }];
307 },
308 MatchField: function MatchField(node) {
309 var _node$alias3;
310
311 hasMatchField = true;
312 return [{
313 key: (_node$alias3 = node.alias) !== null && _node$alias3 !== void 0 ? _node$alias3 : node.name,
314 schemaName: node.name,
315 value: require("@babel/types").nullableTypeAnnotation(require("@babel/types").genericTypeAnnotation(require("@babel/types").identifier('MatchPointer')))
316 }];
317 },
318 FragmentSpread: function FragmentSpread(node) {
319 state.usedFragments.add(node.name);
320 return [{
321 key: '__fragments_' + node.name,
322 ref: node.name
323 }];
324 }
325 }
326 };
327}
328
329function selectionsToMap(selections) {
330 var map = new Map();
331 selections.forEach(function (selection) {
332 var previousSel = map.get(selection.key);
333 map.set(selection.key, previousSel ? mergeSelection(previousSel, selection) : selection);
334 });
335 return map;
336}
337
338function flattenArray(arrayOfArrays) {
339 var result = [];
340 arrayOfArrays.forEach(function (array) {
341 return result.push.apply(result, (0, _toConsumableArray2["default"])(array));
342 });
343 return result;
344}
345
346function generateInputObjectTypes(state) {
347 return Object.keys(state.generatedInputObjectTypes).map(function (typeIdentifier) {
348 var inputObjectType = state.generatedInputObjectTypes[typeIdentifier];
349 !(typeof inputObjectType !== 'string') ? process.env.NODE_ENV !== "production" ? require("fbjs/lib/invariant")(false, 'RelayCompilerFlowGenerator: Expected input object type to have been' + ' defined before calling `generateInputObjectTypes`') : require("fbjs/lib/invariant")(false) : void 0;
350 return require("./RelayFlowBabelFactories").exportType(typeIdentifier, inputObjectType);
351 });
352}
353
354function generateInputVariablesType(node, state) {
355 return require("./RelayFlowBabelFactories").exportType("".concat(node.name, "Variables"), require("./RelayFlowBabelFactories").exactObjectTypeAnnotation(node.argumentDefinitions.map(function (arg) {
356 var property = require("@babel/types").objectTypeProperty(require("@babel/types").identifier(arg.name), require("./RelayFlowTypeTransformers").transformInputType(arg.type, state));
357
358 if (!(arg.type instanceof require("graphql").GraphQLNonNull)) {
359 property.optional = true;
360 }
361
362 return property;
363 })));
364}
365
366function groupRefs(props) {
367 var result = [];
368 var refs = [];
369 props.forEach(function (prop) {
370 if (prop.ref) {
371 refs.push(prop.ref);
372 } else {
373 result.push(prop);
374 }
375 });
376
377 if (refs.length > 0) {
378 var value = require("./RelayFlowBabelFactories").intersectionTypeAnnotation(refs.map(function (ref) {
379 return require("@babel/types").genericTypeAnnotation(require("@babel/types").identifier(getRefTypeName(ref)));
380 }));
381
382 result.push({
383 key: '$fragmentRefs',
384 conditional: false,
385 value: value
386 });
387 }
388
389 return result;
390}
391
392function getFragmentImports(state) {
393 var imports = [];
394
395 if (state.usedFragments.size > 0) {
396 var usedFragments = Array.from(state.usedFragments).sort();
397 var _iteratorNormalCompletion3 = true;
398 var _didIteratorError3 = false;
399 var _iteratorError3 = undefined;
400
401 try {
402 for (var _iterator3 = usedFragments[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
403 var usedFragment = _step3.value;
404 var refTypeName = getRefTypeName(usedFragment);
405
406 if (!state.generatedFragments.has(usedFragment)) {
407 if (state.useHaste && state.existingFragmentNames.has(usedFragment)) {
408 // TODO(T22653277) support non-haste environments when importing
409 // fragments
410 imports.push(require("./RelayFlowBabelFactories").importTypes([refTypeName], usedFragment + '.graphql'));
411 } else if (state.useSingleArtifactDirectory && state.existingFragmentNames.has(usedFragment)) {
412 imports.push(require("./RelayFlowBabelFactories").importTypes([refTypeName], './' + usedFragment + '.graphql'));
413 } else {
414 imports.push(require("./RelayFlowBabelFactories").anyTypeAlias(refTypeName));
415 }
416 }
417 }
418 } catch (err) {
419 _didIteratorError3 = true;
420 _iteratorError3 = err;
421 } finally {
422 try {
423 if (!_iteratorNormalCompletion3 && _iterator3["return"] != null) {
424 _iterator3["return"]();
425 }
426 } finally {
427 if (_didIteratorError3) {
428 throw _iteratorError3;
429 }
430 }
431 }
432 }
433
434 return imports;
435}
436
437function getEnumDefinitions(_ref2) {
438 var enumsHasteModule = _ref2.enumsHasteModule,
439 usedEnums = _ref2.usedEnums,
440 noFutureProofEnums = _ref2.noFutureProofEnums;
441 var enumNames = Object.keys(usedEnums).sort();
442
443 if (enumNames.length === 0) {
444 return [];
445 }
446
447 if (enumsHasteModule) {
448 return [require("./RelayFlowBabelFactories").importTypes(enumNames, enumsHasteModule)];
449 }
450
451 return enumNames.map(function (name) {
452 var values = usedEnums[name].getValues().map(function (_ref3) {
453 var value = _ref3.value;
454 return value;
455 });
456 values.sort();
457
458 if (!noFutureProofEnums) {
459 values.push('%future added value');
460 }
461
462 return require("./RelayFlowBabelFactories").exportType(name, require("@babel/types").unionTypeAnnotation(values.map(function (value) {
463 return require("@babel/types").stringLiteralTypeAnnotation(value);
464 })));
465 });
466}
467
468function getRefTypeName(name) {
469 return "".concat(name, "$ref");
470}
471
472var FLOW_TRANSFORMS = [require("./RelayRelayDirectiveTransform").transform, require("./RelayMaskTransform").transform, require("./RelayMatchTransform").transform, require("./FlattenTransform").transformWithOptions({})];
473module.exports = {
474 generate: require("./GraphQLCompilerProfiler").instrument(generate, 'RelayFlowGenerator.generate'),
475 transforms: FLOW_TRANSFORMS
476};
\No newline at end of file