UNPKG

12.3 kBJavaScriptView Raw
1import { isObjectType, getNamedType, isUnionType, isNonNullType, isScalarType, isListType, isInterfaceType, isEnumType, Kind, } from 'graphql';
2import { getDefinedRootType, getRootTypeNames } from './rootTypes.js';
3let operationVariables = [];
4let fieldTypeMap = new Map();
5function addOperationVariable(variable) {
6 operationVariables.push(variable);
7}
8function resetOperationVariables() {
9 operationVariables = [];
10}
11function resetFieldMap() {
12 fieldTypeMap = new Map();
13}
14export function buildOperationNodeForField({ schema, kind, field, models, ignore = [], depthLimit, circularReferenceDepth, argNames, selectedFields = true, }) {
15 resetOperationVariables();
16 resetFieldMap();
17 const rootTypeNames = getRootTypeNames(schema);
18 const operationNode = buildOperationAndCollectVariables({
19 schema,
20 fieldName: field,
21 kind,
22 models: models || [],
23 ignore,
24 depthLimit: depthLimit || Infinity,
25 circularReferenceDepth: circularReferenceDepth || 1,
26 argNames,
27 selectedFields,
28 rootTypeNames,
29 });
30 // attach variables
31 operationNode.variableDefinitions = [...operationVariables];
32 resetOperationVariables();
33 resetFieldMap();
34 return operationNode;
35}
36function buildOperationAndCollectVariables({ schema, fieldName, kind, models, ignore, depthLimit, circularReferenceDepth, argNames, selectedFields, rootTypeNames, }) {
37 const type = getDefinedRootType(schema, kind);
38 const field = type.getFields()[fieldName];
39 const operationName = `${fieldName}_${kind}`;
40 if (field.args) {
41 for (const arg of field.args) {
42 const argName = arg.name;
43 if (!argNames || argNames.includes(argName)) {
44 addOperationVariable(resolveVariable(arg, argName));
45 }
46 }
47 }
48 return {
49 kind: Kind.OPERATION_DEFINITION,
50 operation: kind,
51 name: {
52 kind: Kind.NAME,
53 value: operationName,
54 },
55 variableDefinitions: [],
56 selectionSet: {
57 kind: Kind.SELECTION_SET,
58 selections: [
59 resolveField({
60 type,
61 field,
62 models,
63 firstCall: true,
64 path: [],
65 ancestors: [],
66 ignore,
67 depthLimit,
68 circularReferenceDepth,
69 schema,
70 depth: 0,
71 argNames,
72 selectedFields,
73 rootTypeNames,
74 }),
75 ],
76 },
77 };
78}
79function resolveSelectionSet({ parent, type, models, firstCall, path, ancestors, ignore, depthLimit, circularReferenceDepth, schema, depth, argNames, selectedFields, rootTypeNames, }) {
80 if (typeof selectedFields === 'boolean' && depth > depthLimit) {
81 return;
82 }
83 if (isUnionType(type)) {
84 const types = type.getTypes();
85 return {
86 kind: Kind.SELECTION_SET,
87 selections: types
88 .filter(t => !hasCircularRef([...ancestors, t], {
89 depth: circularReferenceDepth,
90 }))
91 .map(t => {
92 return {
93 kind: Kind.INLINE_FRAGMENT,
94 typeCondition: {
95 kind: Kind.NAMED_TYPE,
96 name: {
97 kind: Kind.NAME,
98 value: t.name,
99 },
100 },
101 selectionSet: resolveSelectionSet({
102 parent: type,
103 type: t,
104 models,
105 path,
106 ancestors,
107 ignore,
108 depthLimit,
109 circularReferenceDepth,
110 schema,
111 depth,
112 argNames,
113 selectedFields,
114 rootTypeNames,
115 }),
116 };
117 })
118 .filter(fragmentNode => { var _a, _b; return ((_b = (_a = fragmentNode === null || fragmentNode === void 0 ? void 0 : fragmentNode.selectionSet) === null || _a === void 0 ? void 0 : _a.selections) === null || _b === void 0 ? void 0 : _b.length) > 0; }),
119 };
120 }
121 if (isInterfaceType(type)) {
122 const types = Object.values(schema.getTypeMap()).filter((t) => isObjectType(t) && t.getInterfaces().includes(type));
123 return {
124 kind: Kind.SELECTION_SET,
125 selections: types
126 .filter(t => !hasCircularRef([...ancestors, t], {
127 depth: circularReferenceDepth,
128 }))
129 .map(t => {
130 return {
131 kind: Kind.INLINE_FRAGMENT,
132 typeCondition: {
133 kind: Kind.NAMED_TYPE,
134 name: {
135 kind: Kind.NAME,
136 value: t.name,
137 },
138 },
139 selectionSet: resolveSelectionSet({
140 parent: type,
141 type: t,
142 models,
143 path,
144 ancestors,
145 ignore,
146 depthLimit,
147 circularReferenceDepth,
148 schema,
149 depth,
150 argNames,
151 selectedFields,
152 rootTypeNames,
153 }),
154 };
155 })
156 .filter(fragmentNode => { var _a, _b; return ((_b = (_a = fragmentNode === null || fragmentNode === void 0 ? void 0 : fragmentNode.selectionSet) === null || _a === void 0 ? void 0 : _a.selections) === null || _b === void 0 ? void 0 : _b.length) > 0; }),
157 };
158 }
159 if (isObjectType(type) && !rootTypeNames.has(type.name)) {
160 const isIgnored = ignore.includes(type.name) || ignore.includes(`${parent.name}.${path[path.length - 1]}`);
161 const isModel = models.includes(type.name);
162 if (!firstCall && isModel && !isIgnored) {
163 return {
164 kind: Kind.SELECTION_SET,
165 selections: [
166 {
167 kind: Kind.FIELD,
168 name: {
169 kind: Kind.NAME,
170 value: 'id',
171 },
172 },
173 ],
174 };
175 }
176 const fields = type.getFields();
177 return {
178 kind: Kind.SELECTION_SET,
179 selections: Object.keys(fields)
180 .filter(fieldName => {
181 return !hasCircularRef([...ancestors, getNamedType(fields[fieldName].type)], {
182 depth: circularReferenceDepth,
183 });
184 })
185 .map(fieldName => {
186 const selectedSubFields = typeof selectedFields === 'object' ? selectedFields[fieldName] : true;
187 if (selectedSubFields) {
188 return resolveField({
189 type,
190 field: fields[fieldName],
191 models,
192 path: [...path, fieldName],
193 ancestors,
194 ignore,
195 depthLimit,
196 circularReferenceDepth,
197 schema,
198 depth,
199 argNames,
200 selectedFields: selectedSubFields,
201 rootTypeNames,
202 });
203 }
204 return null;
205 })
206 .filter((f) => {
207 var _a, _b;
208 if (f == null) {
209 return false;
210 }
211 else if ('selectionSet' in f) {
212 return !!((_b = (_a = f.selectionSet) === null || _a === void 0 ? void 0 : _a.selections) === null || _b === void 0 ? void 0 : _b.length);
213 }
214 return true;
215 }),
216 };
217 }
218}
219function resolveVariable(arg, name) {
220 function resolveVariableType(type) {
221 if (isListType(type)) {
222 return {
223 kind: Kind.LIST_TYPE,
224 type: resolveVariableType(type.ofType),
225 };
226 }
227 if (isNonNullType(type)) {
228 return {
229 kind: Kind.NON_NULL_TYPE,
230 // for v16 compatibility
231 type: resolveVariableType(type.ofType),
232 };
233 }
234 return {
235 kind: Kind.NAMED_TYPE,
236 name: {
237 kind: Kind.NAME,
238 value: type.name,
239 },
240 };
241 }
242 return {
243 kind: Kind.VARIABLE_DEFINITION,
244 variable: {
245 kind: Kind.VARIABLE,
246 name: {
247 kind: Kind.NAME,
248 value: name || arg.name,
249 },
250 },
251 type: resolveVariableType(arg.type),
252 };
253}
254function getArgumentName(name, path) {
255 return [...path, name].join('_');
256}
257function resolveField({ type, field, models, firstCall, path, ancestors, ignore, depthLimit, circularReferenceDepth, schema, depth, argNames, selectedFields, rootTypeNames, }) {
258 const namedType = getNamedType(field.type);
259 let args = [];
260 let removeField = false;
261 if (field.args && field.args.length) {
262 args = field.args
263 .map(arg => {
264 const argumentName = getArgumentName(arg.name, path);
265 if (argNames && !argNames.includes(argumentName)) {
266 if (isNonNullType(arg.type)) {
267 removeField = true;
268 }
269 return null;
270 }
271 if (!firstCall) {
272 addOperationVariable(resolveVariable(arg, argumentName));
273 }
274 return {
275 kind: Kind.ARGUMENT,
276 name: {
277 kind: Kind.NAME,
278 value: arg.name,
279 },
280 value: {
281 kind: Kind.VARIABLE,
282 name: {
283 kind: Kind.NAME,
284 value: getArgumentName(arg.name, path),
285 },
286 },
287 };
288 })
289 .filter(Boolean);
290 }
291 if (removeField) {
292 return null;
293 }
294 const fieldPath = [...path, field.name];
295 const fieldPathStr = fieldPath.join('.');
296 let fieldName = field.name;
297 if (fieldTypeMap.has(fieldPathStr) && fieldTypeMap.get(fieldPathStr) !== field.type.toString()) {
298 fieldName += field.type.toString().replace('!', 'NonNull');
299 }
300 fieldTypeMap.set(fieldPathStr, field.type.toString());
301 if (!isScalarType(namedType) && !isEnumType(namedType)) {
302 return {
303 kind: Kind.FIELD,
304 name: {
305 kind: Kind.NAME,
306 value: field.name,
307 },
308 ...(fieldName !== field.name && { alias: { kind: Kind.NAME, value: fieldName } }),
309 selectionSet: resolveSelectionSet({
310 parent: type,
311 type: namedType,
312 models,
313 firstCall,
314 path: fieldPath,
315 ancestors: [...ancestors, type],
316 ignore,
317 depthLimit,
318 circularReferenceDepth,
319 schema,
320 depth: depth + 1,
321 argNames,
322 selectedFields,
323 rootTypeNames,
324 }) || undefined,
325 arguments: args,
326 };
327 }
328 return {
329 kind: Kind.FIELD,
330 name: {
331 kind: Kind.NAME,
332 value: field.name,
333 },
334 ...(fieldName !== field.name && { alias: { kind: Kind.NAME, value: fieldName } }),
335 arguments: args,
336 };
337}
338function hasCircularRef(types, config = {
339 depth: 1,
340}) {
341 const type = types[types.length - 1];
342 if (isScalarType(type)) {
343 return false;
344 }
345 const size = types.filter(t => t.name === type.name).length;
346 return size > config.depth;
347}