You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
230 lines
4.9 KiB
230 lines
4.9 KiB
4 months ago
|
'use strict';
|
||
|
|
||
|
Object.defineProperty(exports, '__esModule', {
|
||
|
value: true,
|
||
|
});
|
||
|
exports.collectFields = collectFields;
|
||
|
exports.collectSubfields = collectSubfields;
|
||
|
|
||
|
var _kinds = require('../language/kinds.js');
|
||
|
|
||
|
var _definition = require('../type/definition.js');
|
||
|
|
||
|
var _directives = require('../type/directives.js');
|
||
|
|
||
|
var _typeFromAST = require('../utilities/typeFromAST.js');
|
||
|
|
||
|
var _values = 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 Map();
|
||
|
collectFieldsImpl(
|
||
|
schema,
|
||
|
fragments,
|
||
|
variableValues,
|
||
|
runtimeType,
|
||
|
selectionSet,
|
||
|
fields,
|
||
|
new Set(),
|
||
|
);
|
||
|
return fields;
|
||
|
}
|
||
|
/**
|
||
|
* 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 Map();
|
||
|
const visitedFragmentNames = new Set();
|
||
|
|
||
|
for (const node of fieldNodes) {
|
||
|
if (node.selectionSet) {
|
||
|
collectFieldsImpl(
|
||
|
schema,
|
||
|
fragments,
|
||
|
variableValues,
|
||
|
returnType,
|
||
|
node.selectionSet,
|
||
|
subFieldNodes,
|
||
|
visitedFragmentNames,
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return subFieldNodes;
|
||
|
}
|
||
|
|
||
|
function collectFieldsImpl(
|
||
|
schema,
|
||
|
fragments,
|
||
|
variableValues,
|
||
|
runtimeType,
|
||
|
selectionSet,
|
||
|
fields,
|
||
|
visitedFragmentNames,
|
||
|
) {
|
||
|
for (const selection of selectionSet.selections) {
|
||
|
switch (selection.kind) {
|
||
|
case _kinds.Kind.FIELD: {
|
||
|
if (!shouldIncludeNode(variableValues, selection)) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
const name = getFieldEntryKey(selection);
|
||
|
const fieldList = fields.get(name);
|
||
|
|
||
|
if (fieldList !== undefined) {
|
||
|
fieldList.push(selection);
|
||
|
} else {
|
||
|
fields.set(name, [selection]);
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case _kinds.Kind.INLINE_FRAGMENT: {
|
||
|
if (
|
||
|
!shouldIncludeNode(variableValues, selection) ||
|
||
|
!doesFragmentConditionMatch(schema, selection, runtimeType)
|
||
|
) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
collectFieldsImpl(
|
||
|
schema,
|
||
|
fragments,
|
||
|
variableValues,
|
||
|
runtimeType,
|
||
|
selection.selectionSet,
|
||
|
fields,
|
||
|
visitedFragmentNames,
|
||
|
);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case _kinds.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.getDirectiveValues)(
|
||
|
_directives.GraphQLSkipDirective,
|
||
|
node,
|
||
|
variableValues,
|
||
|
);
|
||
|
|
||
|
if ((skip === null || skip === void 0 ? void 0 : skip.if) === true) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
const include = (0, _values.getDirectiveValues)(
|
||
|
_directives.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.typeFromAST)(
|
||
|
schema,
|
||
|
typeConditionNode,
|
||
|
);
|
||
|
|
||
|
if (conditionalType === type) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if ((0, _definition.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;
|
||
|
}
|