@graphql-tools/graphql
Version:
Fork of GraphQL.js
118 lines (117 loc) • 4.91 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.collectSubfields = exports.collectFields = void 0;
const AccumulatorMap_js_1 = require("../jsutils/AccumulatorMap.js");
const kinds_js_1 = require("../language/kinds.js");
const definition_js_1 = require("../type/definition.js");
const directives_js_1 = require("../type/directives.js");
const typeFromAST_js_1 = require("../utilities/typeFromAST.js");
const values_js_1 = require("./values.js");
/**
* Given a selectionSet, collects all of the fields and returns them.
*
* CollectFields requires the "runtime type" of an object. For a field that
* returns an Interface or Union type, the "runtime type" will be the actual
* object type returned by that field.
*
* @internal
*/
function collectFields(schema, fragments, variableValues, runtimeType, selectionSet) {
const fields = new AccumulatorMap_js_1.AccumulatorMap();
collectFieldsImpl(schema, fragments, variableValues, runtimeType, selectionSet, fields, new Set());
return fields;
}
exports.collectFields = collectFields;
/**
* Given an array of field nodes, collects all of the subfields of the passed
* in fields, and returns them at the end.
*
* CollectSubFields requires the "return type" of an object. For a field that
* returns an Interface or Union type, the "return type" will be the actual
* object type returned by that field.
*
* @internal
*/
function collectSubfields(schema, fragments, variableValues, returnType, fieldNodes) {
const subFieldNodes = new AccumulatorMap_js_1.AccumulatorMap();
const visitedFragmentNames = new Set();
for (const node of fieldNodes) {
if (node.selectionSet) {
collectFieldsImpl(schema, fragments, variableValues, returnType, node.selectionSet, subFieldNodes, visitedFragmentNames);
}
}
return subFieldNodes;
}
exports.collectSubfields = collectSubfields;
function collectFieldsImpl(schema, fragments, variableValues, runtimeType, selectionSet, fields, visitedFragmentNames) {
for (const selection of selectionSet.selections) {
switch (selection.kind) {
case kinds_js_1.Kind.FIELD: {
if (!shouldIncludeNode(variableValues, selection)) {
continue;
}
fields.add(getFieldEntryKey(selection), selection);
break;
}
case kinds_js_1.Kind.INLINE_FRAGMENT: {
if (!shouldIncludeNode(variableValues, selection) ||
!doesFragmentConditionMatch(schema, selection, runtimeType)) {
continue;
}
collectFieldsImpl(schema, fragments, variableValues, runtimeType, selection.selectionSet, fields, visitedFragmentNames);
break;
}
case kinds_js_1.Kind.FRAGMENT_SPREAD: {
const fragName = selection.name.value;
if (visitedFragmentNames.has(fragName) || !shouldIncludeNode(variableValues, selection)) {
continue;
}
visitedFragmentNames.add(fragName);
const fragment = fragments[fragName];
if (!fragment || !doesFragmentConditionMatch(schema, fragment, runtimeType)) {
continue;
}
collectFieldsImpl(schema, fragments, variableValues, runtimeType, fragment.selectionSet, fields, visitedFragmentNames);
break;
}
}
}
}
/**
* Determines if a field should be included based on the `@include` and `@skip`
* directives, where `@skip` has higher precedence than `@include`.
*/
function shouldIncludeNode(variableValues, node) {
const skip = (0, values_js_1.getDirectiveValues)(directives_js_1.GraphQLSkipDirective, node, variableValues);
if ((skip === null || skip === void 0 ? void 0 : skip['if']) === true) {
return false;
}
const include = (0, values_js_1.getDirectiveValues)(directives_js_1.GraphQLIncludeDirective, node, variableValues);
if ((include === null || include === void 0 ? void 0 : include['if']) === false) {
return false;
}
return true;
}
/**
* Determines if a fragment is applicable to the given type.
*/
function doesFragmentConditionMatch(schema, fragment, type) {
const typeConditionNode = fragment.typeCondition;
if (!typeConditionNode) {
return true;
}
const conditionalType = (0, typeFromAST_js_1.typeFromAST)(schema, typeConditionNode);
if (conditionalType === type) {
return true;
}
if ((0, definition_js_1.isAbstractType)(conditionalType)) {
return schema.isSubType(conditionalType, type);
}
return false;
}
/**
* Implements the logic to compute the key of a given field's entry
*/
function getFieldEntryKey(node) {
return node.alias ? node.alias.value : node.name.value;
}