UNPKG

11.6 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
14function generate(node) {
15 if (node.kind !== 'Root' && node.kind !== 'SplitOperation') {
16 throw require("./RelayCompilerError").createCompilerError("NormalizationCodeGenerator: Unsupported AST kind '".concat(node.kind, "'."), [node.loc]);
17 }
18
19 return require("./GraphQLIRVisitor").visit(node, NormalizationCodeGenVisitor);
20}
21
22var NormalizationCodeGenVisitor = {
23 leave: {
24 Root: function Root(node) {
25 return {
26 kind: 'Operation',
27 name: node.name,
28 argumentDefinitions: node.argumentDefinitions,
29 selections: flattenArray(node.selections)
30 };
31 },
32 Request: function Request(node) {
33 throw require("./RelayCompilerError").createCompilerError('NormalizationCodeGenerator: unexpected Request node.');
34 },
35 Fragment: function Fragment(node) {
36 throw require("./RelayCompilerError").createCompilerError('NormalizationCodeGenerator: unexpected Fragment node.');
37 },
38 LocalArgumentDefinition: function LocalArgumentDefinition(node) {
39 return {
40 kind: 'LocalArgument',
41 name: node.name,
42 type: node.type.toString(),
43 defaultValue: node.defaultValue
44 };
45 },
46 RootArgumentDefinition: function RootArgumentDefinition(node) {
47 return {
48 kind: 'RootArgument',
49 name: node.name,
50 type: node.type ? node.type.toString() : null
51 };
52 },
53 Condition: function Condition(node, key, parent, ancestors) {
54 if (node.condition.kind !== 'Variable') {
55 throw require("./RelayCompilerError").createCompilerError("NormalizationCodeGenerator: Expected 'Condition' with static " + 'value to be pruned or inlined', [node.condition.loc]);
56 }
57
58 return {
59 kind: 'Condition',
60 passingValue: node.passingValue,
61 condition: node.condition.variableName,
62 selections: flattenArray(node.selections)
63 };
64 },
65 Defer: function Defer(node, key, parent, ancestors) {
66 var _node$if2;
67
68 if (!(node["if"] == null || node["if"].kind === 'Variable' || node["if"].kind === 'Literal' && node["if"].value === true)) {
69 var _ref, _node$if;
70
71 throw require("./RelayCompilerError").createCompilerError('NormalizationCodeGenerator: Expected @defer `if` condition to be ' + 'a variable, unspecified, or the literal `true`.', [(_ref = (_node$if = node["if"]) === null || _node$if === void 0 ? void 0 : _node$if.loc) !== null && _ref !== void 0 ? _ref : node.loc]);
72 }
73
74 return {
75 "if": ((_node$if2 = node["if"]) === null || _node$if2 === void 0 ? void 0 : _node$if2.kind) === 'Variable' ? node["if"].variableName : null,
76 kind: 'Defer',
77 label: node.label,
78 metadata: node.metadata,
79 selections: flattenArray(node.selections)
80 };
81 },
82 FragmentSpread: function FragmentSpread(node) {
83 // TODO(T37646905) enable this invariant after splitting the
84 // RelayCodeGenerator-test and running the InlineFragmentsTransform on
85 // normalization ASTs.
86 //
87 // throw new Error(
88 // 'NormalizationCodeGenerator: unexpected FragmentSpread node.',
89 // );
90 return [];
91 },
92 InlineFragment: function InlineFragment(node) {
93 return {
94 kind: 'InlineFragment',
95 type: node.typeCondition.toString(),
96 selections: flattenArray(node.selections)
97 };
98 },
99 LinkedField: function LinkedField(node) {
100 // Note: it is important that the arguments of this field be sorted to
101 // ensure stable generation of storage keys for equivalent arguments
102 // which may have originally appeared in different orders across an app.
103 var handles = node.handles && node.handles.map(function (handle) {
104 return {
105 kind: 'LinkedHandle',
106 alias: node.alias,
107 name: node.name,
108 args: valuesOrNull(sortByName(node.args)),
109 handle: handle.name,
110 key: handle.key,
111 filters: handle.filters
112 };
113 }) || [];
114
115 var type = require("./GraphQLSchemaUtils").getRawType(node.type);
116
117 var field = {
118 kind: 'LinkedField',
119 alias: node.alias,
120 name: node.name,
121 storageKey: null,
122 args: valuesOrNull(sortByName(node.args)),
123 concreteType: !require("./GraphQLSchemaUtils").isAbstractType(type) ? type.toString() : null,
124 plural: isPlural(node.type),
125 selections: flattenArray(node.selections)
126 }; // Precompute storageKey if possible
127
128 var storageKey = getStaticStorageKey(field, node.metadata);
129
130 if (storageKey) {
131 field = (0, _objectSpread2["default"])({}, field, {
132 storageKey: storageKey
133 });
134 }
135
136 return [field].concat(handles);
137 },
138 MatchField: function MatchField(node, key, parent, ancestors) {
139 var selections = flattenArray(node.selections);
140 var matchesByType = {};
141 selections.forEach(function (selection) {
142 var _regExpMatch$;
143
144 if (selection.kind === 'ScalarField' && selection.name === '__typename') {
145 // The RelayGenerateTypename transform will add a __typename selection
146 // to the selections of the match field.
147 return;
148 }
149
150 if (selection.kind !== 'MatchBranch') {
151 throw require("./RelayCompilerError").createCompilerError("NormalizationCodeGenerator: Expected selection for MatchField '".concat(node.name, "' to be a 'MatchBranch', got '").concat(selection.kind, "'."), [selection.loc]);
152 }
153
154 if (matchesByType.hasOwnProperty(selection.type)) {
155 throw require("./RelayCompilerError").createCompilerError('NormalizationCodeGenerator: Each @match type can appear at-most ' + "once. Type '".concat(String(selection.type), "' was duplicated."), selection.type, [selection.loc]);
156 }
157
158 var fragmentName = selection.name;
159 var regExpMatch = fragmentName.match(/^([a-zA-Z][a-zA-Z0-9]*)(?:_([a-zA-Z][_a-zA-Z0-9]*))?$/);
160
161 if (!regExpMatch) {
162 throw require("./RelayCompilerError").createCompilerError('NormalizationCodeGenerator: @match fragments should be named ' + "'FragmentName_propName', got '".concat(fragmentName, "'."), [selection.loc]);
163 }
164
165 var fragmentPropName = (_regExpMatch$ = regExpMatch[2]) !== null && _regExpMatch$ !== void 0 ? _regExpMatch$ : 'matchData';
166 matchesByType[selection.type] = {
167 fragmentPropName: fragmentPropName,
168 fragmentName: fragmentName
169 };
170 });
171 var field = {
172 kind: 'MatchField',
173 alias: node.alias,
174 name: node.name,
175 storageKey: null,
176 args: valuesOrNull(sortByName(node.args)),
177 matchesByType: matchesByType
178 }; // Precompute storageKey if possible
179
180 var storageKey = getStaticStorageKey(field, node.metadata);
181
182 if (storageKey) {
183 field = (0, _objectSpread2["default"])({}, field, {
184 storageKey: storageKey
185 });
186 }
187
188 return field;
189 },
190 ScalarField: function ScalarField(node) {
191 // Note: it is important that the arguments of this field be sorted to
192 // ensure stable generation of storage keys for equivalent arguments
193 // which may have originally appeared in different orders across an app.
194 var handles = node.handles && node.handles.map(function (handle) {
195 return {
196 kind: 'ScalarHandle',
197 alias: node.alias,
198 name: node.name,
199 args: valuesOrNull(sortByName(node.args)),
200 handle: handle.name,
201 key: handle.key,
202 filters: handle.filters
203 };
204 }) || [];
205 var field = {
206 kind: 'ScalarField',
207 alias: node.alias,
208 name: node.name,
209 args: valuesOrNull(sortByName(node.args)),
210 storageKey: null
211 }; // Precompute storageKey if possible
212
213 var storageKey = getStaticStorageKey(field, node.metadata);
214
215 if (storageKey) {
216 field = (0, _objectSpread2["default"])({}, field, {
217 storageKey: storageKey
218 });
219 }
220
221 return [field].concat(handles);
222 },
223 SplitOperation: function SplitOperation(node, key, parent) {
224 return {
225 kind: 'SplitOperation',
226 name: node.name,
227 metadata: node.metadata,
228 selections: flattenArray(node.selections)
229 };
230 },
231 Stream: function Stream(node, key, parent, ancestors) {
232 var _node$if4;
233
234 if (!(node["if"] == null || node["if"].kind === 'Variable' || node["if"].kind === 'Literal' && node["if"].value === true)) {
235 var _ref2, _node$if3;
236
237 throw require("./RelayCompilerError").createCompilerError('NormalizationCodeGenerator: Expected @stream `if` condition to be ' + 'a variable, unspecified, or the literal `true`.', [(_ref2 = (_node$if3 = node["if"]) === null || _node$if3 === void 0 ? void 0 : _node$if3.loc) !== null && _ref2 !== void 0 ? _ref2 : node.loc]);
238 }
239
240 return {
241 "if": ((_node$if4 = node["if"]) === null || _node$if4 === void 0 ? void 0 : _node$if4.kind) === 'Variable' ? node["if"].variableName : null,
242 kind: 'Stream',
243 label: node.label,
244 metadata: node.metadata,
245 selections: flattenArray(node.selections)
246 };
247 },
248 Variable: function Variable(node, key, parent) {
249 return {
250 kind: 'Variable',
251 name: parent.name,
252 variableName: node.variableName,
253 type: parent.type ? parent.type.toString() : null
254 };
255 },
256 Literal: function Literal(node, key, parent) {
257 return {
258 kind: 'Literal',
259 name: parent.name,
260 value: require("relay-runtime").stableCopy(node.value),
261 type: parent.type ? parent.type.toString() : null
262 };
263 },
264 Argument: function Argument(node, key, parent, ancestors) {
265 if (!['Variable', 'Literal'].includes(node.value.kind)) {
266 throw require("./RelayCompilerError").createUserError('RelayCodeGenerator: Complex argument values (Lists or ' + 'InputObjects with nested variables) are not supported.', [node.value.loc]);
267 }
268
269 return node.value.value !== null ? node.value : null;
270 }
271 }
272};
273
274function isPlural(type) {
275 return require("./GraphQLSchemaUtils").getNullableType(type) instanceof require("graphql").GraphQLList;
276}
277
278function valuesOrNull(array) {
279 return !array || array.length === 0 ? null : array;
280}
281
282function flattenArray(array) {
283 return array ? Array.prototype.concat.apply([], array) : [];
284}
285
286function sortByName(array) {
287 return array instanceof Array ? array.slice().sort(function (a, b) {
288 return a.name < b.name ? -1 : a.name > b.name ? 1 : 0;
289 }) : array;
290}
291/**
292 * Pre-computes storage key if possible and advantageous. Storage keys are
293 * generated for fields with supplied arguments that are all statically known
294 * (ie. literals, no variables) at build time.
295 */
296
297
298function getStaticStorageKey(field, metadata) {
299 var metadataStorageKey = metadata === null || metadata === void 0 ? void 0 : metadata.storageKey;
300
301 if (typeof metadataStorageKey === 'string') {
302 return metadataStorageKey;
303 }
304
305 if (!field.args || field.args.length === 0 || field.args.some(function (arg) {
306 return arg.kind !== 'Literal';
307 })) {
308 return null;
309 }
310
311 return require("relay-runtime").getStorageKey(field, {});
312}
313
314module.exports = {
315 generate: generate
316};
\No newline at end of file