UNPKG

3.85 kBJavaScriptView Raw
1import { memoize5 } from './memoize.js';
2import { Kind, getDirectiveValues, GraphQLSkipDirective, GraphQLIncludeDirective, isAbstractType, typeFromAST, } from 'graphql';
3// Taken from GraphQL-JS v16 for backwards compat
4export function collectFields(schema, fragments, variableValues, runtimeType, selectionSet, fields, visitedFragmentNames) {
5 for (const selection of selectionSet.selections) {
6 switch (selection.kind) {
7 case Kind.FIELD: {
8 if (!shouldIncludeNode(variableValues, selection)) {
9 continue;
10 }
11 const name = getFieldEntryKey(selection);
12 const fieldList = fields.get(name);
13 if (fieldList !== undefined) {
14 fieldList.push(selection);
15 }
16 else {
17 fields.set(name, [selection]);
18 }
19 break;
20 }
21 case Kind.INLINE_FRAGMENT: {
22 if (!shouldIncludeNode(variableValues, selection) ||
23 !doesFragmentConditionMatch(schema, selection, runtimeType)) {
24 continue;
25 }
26 collectFields(schema, fragments, variableValues, runtimeType, selection.selectionSet, fields, visitedFragmentNames);
27 break;
28 }
29 case Kind.FRAGMENT_SPREAD: {
30 const fragName = selection.name.value;
31 if (visitedFragmentNames.has(fragName) || !shouldIncludeNode(variableValues, selection)) {
32 continue;
33 }
34 visitedFragmentNames.add(fragName);
35 const fragment = fragments[fragName];
36 if (!fragment || !doesFragmentConditionMatch(schema, fragment, runtimeType)) {
37 continue;
38 }
39 collectFields(schema, fragments, variableValues, runtimeType, fragment.selectionSet, fields, visitedFragmentNames);
40 break;
41 }
42 }
43 }
44 return fields;
45}
46/**
47 * Determines if a field should be included based on the `@include` and `@skip`
48 * directives, where `@skip` has higher precedence than `@include`.
49 */
50function shouldIncludeNode(variableValues, node) {
51 const skip = getDirectiveValues(GraphQLSkipDirective, node, variableValues);
52 if ((skip === null || skip === void 0 ? void 0 : skip['if']) === true) {
53 return false;
54 }
55 const include = getDirectiveValues(GraphQLIncludeDirective, node, variableValues);
56 if ((include === null || include === void 0 ? void 0 : include['if']) === false) {
57 return false;
58 }
59 return true;
60}
61/**
62 * Determines if a fragment is applicable to the given type.
63 */
64function doesFragmentConditionMatch(schema, fragment, type) {
65 const typeConditionNode = fragment.typeCondition;
66 if (!typeConditionNode) {
67 return true;
68 }
69 const conditionalType = typeFromAST(schema, typeConditionNode);
70 if (conditionalType === type) {
71 return true;
72 }
73 if (isAbstractType(conditionalType)) {
74 const possibleTypes = schema.getPossibleTypes(conditionalType);
75 return possibleTypes.includes(type);
76 }
77 return false;
78}
79/**
80 * Implements the logic to compute the key of a given field's entry
81 */
82function getFieldEntryKey(node) {
83 return node.alias ? node.alias.value : node.name.value;
84}
85export const collectSubFields = memoize5(function collectSubFields(schema, fragments, variableValues, type, fieldNodes) {
86 const subFieldNodes = new Map();
87 const visitedFragmentNames = new Set();
88 for (const fieldNode of fieldNodes) {
89 if (fieldNode.selectionSet) {
90 collectFields(schema, fragments, variableValues, type, fieldNode.selectionSet, subFieldNodes, visitedFragmentNames);
91 }
92 }
93 return subFieldNodes;
94});