UNPKG

@graphql-tools/merge

Version:

A set of utils for faster development of GraphQL tools

82 lines (81 loc) 3.72 kB
import { extractType, isWrappingTypeNode, isListTypeNode, isNonNullTypeNode, printTypeNode } from './utils.js'; import { mergeDirectives } from './directives.js'; import { compareNodes } from '@graphql-tools/utils'; import { mergeArguments } from './arguments.js'; function fieldAlreadyExists(fieldsArr, otherField, config) { const result = fieldsArr.find(field => field.name.value === otherField.name.value); if (result && !(config === null || config === void 0 ? void 0 : config.ignoreFieldConflicts)) { const t1 = extractType(result.type); const t2 = extractType(otherField.type); if (t1.name.value !== t2.name.value) { throw new Error(`Field "${otherField.name.value}" already defined with a different type. Declared as "${t1.name.value}", but you tried to override with "${t2.name.value}"`); } } return !!result; } export function mergeFields(type, f1, f2, config) { const result = []; if (f2 != null) { result.push(...f2); } if (f1 != null) { for (const field of f1) { if (fieldAlreadyExists(result, field, config)) { const existing = result.find((f) => f.name.value === field.name.value); if (!(config === null || config === void 0 ? void 0 : config.ignoreFieldConflicts)) { if (config === null || config === void 0 ? void 0 : config.throwOnConflict) { preventConflicts(type, existing, field, false); } else { preventConflicts(type, existing, field, true); } if (isNonNullTypeNode(field.type) && !isNonNullTypeNode(existing.type)) { existing.type = field.type; } } existing.arguments = mergeArguments(field['arguments'] || [], existing.arguments || [], config); existing.directives = mergeDirectives(field.directives, existing.directives, config); existing.description = field.description || existing.description; } else { result.push(field); } } } if (config && config.sort) { result.sort(compareNodes); } if (config && config.exclusions) { const exclusions = config.exclusions; return result.filter(field => !exclusions.includes(`${type.name.value}.${field.name.value}`)); } return result; } function preventConflicts(type, a, b, ignoreNullability = false) { const aType = printTypeNode(a.type); const bType = printTypeNode(b.type); if (aType !== bType && !safeChangeForFieldType(a.type, b.type, ignoreNullability)) { throw new Error(`Field '${type.name.value}.${a.name.value}' changed type from '${aType}' to '${bType}'`); } } function safeChangeForFieldType(oldType, newType, ignoreNullability = false) { // both are named if (!isWrappingTypeNode(oldType) && !isWrappingTypeNode(newType)) { return oldType.toString() === newType.toString(); } // new is non-null if (isNonNullTypeNode(newType)) { const ofType = isNonNullTypeNode(oldType) ? oldType.type : oldType; return safeChangeForFieldType(ofType, newType.type); } // old is non-null if (isNonNullTypeNode(oldType)) { return safeChangeForFieldType(newType, oldType, ignoreNullability); } // old is list if (isListTypeNode(oldType)) { return ((isListTypeNode(newType) && safeChangeForFieldType(oldType.type, newType.type)) || (isNonNullTypeNode(newType) && safeChangeForFieldType(oldType, newType['type']))); } return false; }