UNPKG

6.18 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 * All rights reserved.
8 *
9 *
10 * @format
11 */
12'use strict';
13
14var _objectSpread2 = require("@babel/runtime/helpers/interopRequireDefault")(require("@babel/runtime/helpers/objectSpread"));
15
16/**
17 * A transform that inlines fragment spreads with the @relay(mask: false)
18 * directive.
19 */
20function relayMaskTransform(context) {
21 return require("./GraphQLIRTransformer").transform(context, {
22 FragmentSpread: visitFragmentSpread,
23 Fragment: visitFragment
24 }, function () {
25 return {
26 reachableArguments: []
27 };
28 });
29}
30
31function visitFragment(fragment, state) {
32 var result = this.traverse(fragment, state);
33
34 if (state.reachableArguments.length === 0) {
35 return result;
36 }
37
38 var schema = this.getContext().serverSchema;
39 var joinedArgumentDefinitions = joinFragmentArgumentDefinitions(schema, fragment, state.reachableArguments);
40 return (0, _objectSpread2["default"])({}, result, {
41 argumentDefinitions: joinedArgumentDefinitions
42 });
43}
44
45function visitFragmentSpread(fragmentSpread, state) {
46 if (!isUnmaskedSpread(fragmentSpread)) {
47 return fragmentSpread;
48 }
49
50 !(fragmentSpread.args.length === 0) ? process.env.NODE_ENV !== "production" ? require("fbjs/lib/invariant")(false, 'RelayMaskTransform: Cannot unmask fragment spread `%s` with ' + 'arguments. Use the `ApplyFragmentArgumentTransform` before flattening', fragmentSpread.name) : require("fbjs/lib/invariant")(false) : void 0;
51 var context = this.getContext();
52 var fragment = context.getFragment(fragmentSpread.name);
53 var result = {
54 kind: 'InlineFragment',
55 directives: fragmentSpread.directives,
56 loc: {
57 kind: 'Derived',
58 source: fragmentSpread.loc
59 },
60 metadata: fragmentSpread.metadata,
61 selections: fragment.selections,
62 typeCondition: fragment.type
63 };
64 !!fragment.argumentDefinitions.find(function (argDef) {
65 return argDef.kind === 'LocalArgumentDefinition';
66 }) ? process.env.NODE_ENV !== "production" ? require("fbjs/lib/invariant")(false, 'RelayMaskTransform: Cannot unmask fragment spread `%s` because it has local ' + 'argument definition.', fragmentSpread.name) : require("fbjs/lib/invariant")(false) : void 0; // Note: defer validating arguments to the containing fragment in order
67 // to list all invalid variables/arguments instead of only one.
68
69 var _iteratorNormalCompletion = true;
70 var _didIteratorError = false;
71 var _iteratorError = undefined;
72
73 try {
74 for (var _iterator = fragment.argumentDefinitions[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
75 var argDef = _step.value;
76 state.reachableArguments.push({
77 argDef: argDef,
78 source: fragmentSpread.name
79 });
80 }
81 } catch (err) {
82 _didIteratorError = true;
83 _iteratorError = err;
84 } finally {
85 try {
86 if (!_iteratorNormalCompletion && _iterator["return"] != null) {
87 _iterator["return"]();
88 }
89 } finally {
90 if (_didIteratorError) {
91 throw _iteratorError;
92 }
93 }
94 }
95
96 return this.traverse(result, state);
97}
98/**
99 * @private
100 */
101
102
103function isUnmaskedSpread(spread) {
104 return Boolean(spread.metadata && spread.metadata.mask === false);
105}
106/**
107 * @private
108 *
109 * Attempts to join the argument definitions for a root fragment
110 * and any unmasked fragment spreads reachable from that root fragment,
111 * returning a combined list of arguments or throwing if the same
112 * variable(s) are used in incompatible ways in different fragments.
113 */
114
115
116function joinFragmentArgumentDefinitions(schema, fragment, reachableArguments) {
117 var joinedArgumentDefinitions = new Map();
118 fragment.argumentDefinitions.forEach(function (prevArgDef) {
119 joinedArgumentDefinitions.set(prevArgDef.name, prevArgDef);
120 });
121 var errors = [];
122 reachableArguments.forEach(function (nextArg) {
123 var nextArgDef = nextArg.argDef,
124 source = nextArg.source;
125 var prevArgDef = joinedArgumentDefinitions.get(nextArgDef.name);
126
127 if (prevArgDef) {
128 var joinedArgDef = joinArgumentDefinition(schema, prevArgDef, nextArgDef);
129
130 if (joinedArgDef === null) {
131 errors.push("Variable `$".concat(nextArgDef.name, "` in `").concat(source, "`"));
132 } else {
133 joinedArgumentDefinitions.set(joinedArgDef.name, joinedArgDef);
134 }
135 } else {
136 joinedArgumentDefinitions.set(nextArgDef.name, nextArgDef);
137 }
138 });
139
140 if (errors.length) {
141 throw new Error('RelayMaskTransform: Cannot unmask one or more fragments in ' + "`".concat(fragment.name, "`, the following variables are referenced more ") + 'than once with incompatible kinds/types:\n' + errors.map(function (msg) {
142 return "* ".concat(msg);
143 }).join('\n'));
144 }
145
146 return Array.from(joinedArgumentDefinitions.values());
147}
148/**
149 * @private
150 *
151 * Attempts to join two argument definitions, returning a single argument
152 * definition that is compatible with both of the inputs:
153 * - If the kind, name, or defaultValue is different then the arguments
154 * cannot be joined, indicated by returning null.
155 * - If either of next/prev is a subtype of the other, return the one
156 * that is the subtype: a more narrow type can flow into a more general
157 * type but not the inverse.
158 * - Otherwise there is no subtyping relation between prev/next, so return
159 * null to indicate they cannot be joined.
160 */
161
162
163function joinArgumentDefinition(schema, prevArgDef, nextArgDef) {
164 if (prevArgDef.kind !== nextArgDef.kind || prevArgDef.name !== nextArgDef.name || // Only LocalArgumentDefinition defines defaultValue
165 prevArgDef.defaultValue !== nextArgDef.defaultValue) {
166 return null;
167 } else if (require("graphql").isTypeSubTypeOf(schema, nextArgDef.type, prevArgDef.type)) {
168 // prevArgDef is less strict than nextArgDef
169 return nextArgDef;
170 } else if (require("graphql").isTypeSubTypeOf(schema, prevArgDef.type, nextArgDef.type)) {
171 return prevArgDef;
172 } else {
173 return null;
174 }
175}
176
177module.exports = {
178 transform: relayMaskTransform
179};
\No newline at end of file