collectFields.js 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. 'use strict';
  2. Object.defineProperty(exports, '__esModule', {
  3. value: true,
  4. });
  5. exports.collectFields = collectFields;
  6. exports.collectSubfields = collectSubfields;
  7. var _kinds = require('../language/kinds.js');
  8. var _definition = require('../type/definition.js');
  9. var _directives = require('../type/directives.js');
  10. var _typeFromAST = require('../utilities/typeFromAST.js');
  11. var _values = require('./values.js');
  12. /**
  13. * Given a selectionSet, collects all of the fields and returns them.
  14. *
  15. * CollectFields requires the "runtime type" of an object. For a field that
  16. * returns an Interface or Union type, the "runtime type" will be the actual
  17. * object type returned by that field.
  18. *
  19. * @internal
  20. */
  21. function collectFields(
  22. schema,
  23. fragments,
  24. variableValues,
  25. runtimeType,
  26. selectionSet,
  27. ) {
  28. const fields = new Map();
  29. collectFieldsImpl(
  30. schema,
  31. fragments,
  32. variableValues,
  33. runtimeType,
  34. selectionSet,
  35. fields,
  36. new Set(),
  37. );
  38. return fields;
  39. }
  40. /**
  41. * Given an array of field nodes, collects all of the subfields of the passed
  42. * in fields, and returns them at the end.
  43. *
  44. * CollectSubFields requires the "return type" of an object. For a field that
  45. * returns an Interface or Union type, the "return type" will be the actual
  46. * object type returned by that field.
  47. *
  48. * @internal
  49. */
  50. function collectSubfields(
  51. schema,
  52. fragments,
  53. variableValues,
  54. returnType,
  55. fieldNodes,
  56. ) {
  57. const subFieldNodes = new Map();
  58. const visitedFragmentNames = new Set();
  59. for (const node of fieldNodes) {
  60. if (node.selectionSet) {
  61. collectFieldsImpl(
  62. schema,
  63. fragments,
  64. variableValues,
  65. returnType,
  66. node.selectionSet,
  67. subFieldNodes,
  68. visitedFragmentNames,
  69. );
  70. }
  71. }
  72. return subFieldNodes;
  73. }
  74. function collectFieldsImpl(
  75. schema,
  76. fragments,
  77. variableValues,
  78. runtimeType,
  79. selectionSet,
  80. fields,
  81. visitedFragmentNames,
  82. ) {
  83. for (const selection of selectionSet.selections) {
  84. switch (selection.kind) {
  85. case _kinds.Kind.FIELD: {
  86. if (!shouldIncludeNode(variableValues, selection)) {
  87. continue;
  88. }
  89. const name = getFieldEntryKey(selection);
  90. const fieldList = fields.get(name);
  91. if (fieldList !== undefined) {
  92. fieldList.push(selection);
  93. } else {
  94. fields.set(name, [selection]);
  95. }
  96. break;
  97. }
  98. case _kinds.Kind.INLINE_FRAGMENT: {
  99. if (
  100. !shouldIncludeNode(variableValues, selection) ||
  101. !doesFragmentConditionMatch(schema, selection, runtimeType)
  102. ) {
  103. continue;
  104. }
  105. collectFieldsImpl(
  106. schema,
  107. fragments,
  108. variableValues,
  109. runtimeType,
  110. selection.selectionSet,
  111. fields,
  112. visitedFragmentNames,
  113. );
  114. break;
  115. }
  116. case _kinds.Kind.FRAGMENT_SPREAD: {
  117. const fragName = selection.name.value;
  118. if (
  119. visitedFragmentNames.has(fragName) ||
  120. !shouldIncludeNode(variableValues, selection)
  121. ) {
  122. continue;
  123. }
  124. visitedFragmentNames.add(fragName);
  125. const fragment = fragments[fragName];
  126. if (
  127. !fragment ||
  128. !doesFragmentConditionMatch(schema, fragment, runtimeType)
  129. ) {
  130. continue;
  131. }
  132. collectFieldsImpl(
  133. schema,
  134. fragments,
  135. variableValues,
  136. runtimeType,
  137. fragment.selectionSet,
  138. fields,
  139. visitedFragmentNames,
  140. );
  141. break;
  142. }
  143. }
  144. }
  145. }
  146. /**
  147. * Determines if a field should be included based on the `@include` and `@skip`
  148. * directives, where `@skip` has higher precedence than `@include`.
  149. */
  150. function shouldIncludeNode(variableValues, node) {
  151. const skip = (0, _values.getDirectiveValues)(
  152. _directives.GraphQLSkipDirective,
  153. node,
  154. variableValues,
  155. );
  156. if ((skip === null || skip === void 0 ? void 0 : skip.if) === true) {
  157. return false;
  158. }
  159. const include = (0, _values.getDirectiveValues)(
  160. _directives.GraphQLIncludeDirective,
  161. node,
  162. variableValues,
  163. );
  164. if (
  165. (include === null || include === void 0 ? void 0 : include.if) === false
  166. ) {
  167. return false;
  168. }
  169. return true;
  170. }
  171. /**
  172. * Determines if a fragment is applicable to the given type.
  173. */
  174. function doesFragmentConditionMatch(schema, fragment, type) {
  175. const typeConditionNode = fragment.typeCondition;
  176. if (!typeConditionNode) {
  177. return true;
  178. }
  179. const conditionalType = (0, _typeFromAST.typeFromAST)(
  180. schema,
  181. typeConditionNode,
  182. );
  183. if (conditionalType === type) {
  184. return true;
  185. }
  186. if ((0, _definition.isAbstractType)(conditionalType)) {
  187. return schema.isSubType(conditionalType, type);
  188. }
  189. return false;
  190. }
  191. /**
  192. * Implements the logic to compute the key of a given field's entry
  193. */
  194. function getFieldEntryKey(node) {
  195. return node.alias ? node.alias.value : node.name.value;
  196. }