UNPKG

@graphql-tools/delegate

Version:

A set of utils for faster development of GraphQL tools

116 lines (115 loc) 4.71 kB
import { getNamedType, isAbstractType, isInterfaceType, isLeafType, isObjectType, isUnionType, Kind, visit, } from 'graphql'; import { memoize4 } from '@graphql-tools/utils'; export const extractUnavailableFieldsFromSelectionSet = memoize4(function extractUnavailableFieldsFromSelectionSet(schema, fieldType, fieldSelectionSet, shouldAdd) { if (isLeafType(fieldType)) { return []; } if (isUnionType(fieldType)) { const unavailableSelections = []; for (const type of fieldType.getTypes()) { // Exclude other inline fragments const fieldSelectionExcluded = { ...fieldSelectionSet, selections: fieldSelectionSet.selections.filter(selection => selection.kind === Kind.INLINE_FRAGMENT ? selection.typeCondition ? selection.typeCondition.name.value === type.name : false : true), }; unavailableSelections.push(...extractUnavailableFieldsFromSelectionSet(schema, type, fieldSelectionExcluded, shouldAdd)); } return unavailableSelections; } const subFields = fieldType.getFields(); const unavailableSelections = []; for (const selection of fieldSelectionSet.selections) { if (selection.kind === Kind.FIELD) { if (selection.name.value === '__typename') { continue; } const fieldName = selection.name.value; const selectionField = subFields[fieldName]; if (!selectionField) { if (shouldAdd(fieldType, selection)) { unavailableSelections.push(selection); } } else { const unavailableSubFields = extractUnavailableFields(schema, selectionField, selection, shouldAdd); if (unavailableSubFields.length) { unavailableSelections.push({ ...selection, selectionSet: { kind: Kind.SELECTION_SET, selections: unavailableSubFields, }, }); } } } else if (selection.kind === Kind.INLINE_FRAGMENT) { const subFieldType = selection.typeCondition ? schema.getType(selection.typeCondition.name.value) : fieldType; if (subFieldType === fieldType || ((isObjectType(subFieldType) || isInterfaceType(subFieldType)) && isAbstractType(fieldType) && schema.isSubType(fieldType, subFieldType))) { const unavailableFields = extractUnavailableFieldsFromSelectionSet(schema, subFieldType, selection.selectionSet, shouldAdd); if (unavailableFields.length) { unavailableSelections.push({ ...selection, selectionSet: { kind: Kind.SELECTION_SET, selections: unavailableFields, }, }); } } } } return unavailableSelections; }); export const extractUnavailableFields = memoize4(function extractUnavailableFields(schema, field, fieldNode, shouldAdd) { if (fieldNode.selectionSet) { const fieldType = getNamedType(field.type); return extractUnavailableFieldsFromSelectionSet(schema, fieldType, fieldNode.selectionSet, shouldAdd); } return []; }); function getByPath(object, path) { let current = object; for (const pathSegment of path) { if (current == null) { return; } current = current[pathSegment]; } return current; } export function subtractSelectionSets(selectionSetA, selectionSetB) { return visit(selectionSetA, { [Kind.FIELD]: { enter(node, _key, _parent, path) { if (!node.selectionSet && getByPath(selectionSetB, path.slice(0, -1))?.some(selection => selection.kind === Kind.FIELD && selection.name.value === node.name.value)) { return null; } }, }, [Kind.SELECTION_SET]: { leave(node) { if (node.selections.length === 0) { return null; } }, }, [Kind.INLINE_FRAGMENT]: { leave(node) { if (node.selectionSet?.selections.length === 0) { return null; } }, }, }); }