UNPKG

@graphql-inspector/core

Version:

Tooling for GraphQL. Compare GraphQL Schemas, check documents, find breaking changes, find similar types.

196 lines (195 loc) • 7.46 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.safeChangeForField = safeChangeForField; exports.safeChangeForInputValue = safeChangeForInputValue; exports.getKind = getKind; exports.getTypePrefix = getTypePrefix; exports.isPrimitive = isPrimitive; exports.isForIntrospection = isForIntrospection; exports.findDeprecatedUsages = findDeprecatedUsages; exports.removeFieldIfDirectives = removeFieldIfDirectives; exports.removeDirectives = removeDirectives; exports.getReachableTypes = getReachableTypes; const graphql_1 = require("graphql"); const is_deprecated_js_1 = require("./is-deprecated.js"); function safeChangeForField(oldType, newType) { if (!(0, graphql_1.isWrappingType)(oldType) && !(0, graphql_1.isWrappingType)(newType)) { return oldType.toString() === newType.toString(); } if ((0, graphql_1.isNonNullType)(newType)) { const ofType = (0, graphql_1.isNonNullType)(oldType) ? oldType.ofType : oldType; return safeChangeForField(ofType, newType.ofType); } if ((0, graphql_1.isListType)(oldType)) { return (((0, graphql_1.isListType)(newType) && safeChangeForField(oldType.ofType, newType.ofType)) || ((0, graphql_1.isNonNullType)(newType) && safeChangeForField(oldType, newType.ofType))); } return false; } function safeChangeForInputValue(oldType, newType) { if (!(0, graphql_1.isWrappingType)(oldType) && !(0, graphql_1.isWrappingType)(newType)) { return oldType.toString() === newType.toString(); } if ((0, graphql_1.isListType)(oldType) && (0, graphql_1.isListType)(newType)) { return safeChangeForInputValue(oldType.ofType, newType.ofType); } if ((0, graphql_1.isNonNullType)(oldType)) { const ofType = (0, graphql_1.isNonNullType)(newType) ? newType.ofType : newType; return safeChangeForInputValue(oldType.ofType, ofType); } return false; } function getKind(type) { const node = type.astNode; return node?.kind || ''; } function getTypePrefix(type) { const kind = getKind(type); const kindsMap = { [graphql_1.Kind.SCALAR_TYPE_DEFINITION]: 'scalar', [graphql_1.Kind.OBJECT_TYPE_DEFINITION]: 'type', [graphql_1.Kind.INTERFACE_TYPE_DEFINITION]: 'interface', [graphql_1.Kind.UNION_TYPE_DEFINITION]: 'union', [graphql_1.Kind.ENUM_TYPE_DEFINITION]: 'enum', [graphql_1.Kind.INPUT_OBJECT_TYPE_DEFINITION]: 'input', }; return kindsMap[kind.toString()]; } function isPrimitive(type) { return ['String', 'Int', 'Float', 'Boolean', 'ID'].includes(typeof type === 'string' ? type : type.name); } function isForIntrospection(type) { return [ '__Schema', '__Type', '__TypeKind', '__Field', '__InputValue', '__EnumValue', '__Directive', '__DirectiveLocation', ].includes(typeof type === 'string' ? type : type.name); } function findDeprecatedUsages(schema, ast) { const errors = []; const typeInfo = new graphql_1.TypeInfo(schema); (0, graphql_1.visit)(ast, (0, graphql_1.visitWithTypeInfo)(typeInfo, { Argument(node) { const argument = typeInfo.getArgument(); if (argument) { const reason = argument.deprecationReason; if (reason) { const fieldDef = typeInfo.getFieldDef(); if (fieldDef) { errors.push(new graphql_1.GraphQLError(`The argument '${argument?.name}' of '${fieldDef.name}' is deprecated. ${reason}`, [node])); } } } }, Field(node) { const fieldDef = typeInfo.getFieldDef(); if (fieldDef && (0, is_deprecated_js_1.isDeprecated)(fieldDef)) { const parentType = typeInfo.getParentType(); if (parentType) { const reason = fieldDef.deprecationReason; errors.push(new graphql_1.GraphQLError(`The field '${parentType.name}.${fieldDef.name}' is deprecated.${reason ? ' ' + reason : ''}`, [node])); } } }, EnumValue(node) { const enumVal = typeInfo.getEnumValue(); if (enumVal && (0, is_deprecated_js_1.isDeprecated)(enumVal)) { const type = (0, graphql_1.getNamedType)(typeInfo.getInputType()); if (type) { const reason = enumVal.deprecationReason; errors.push(new graphql_1.GraphQLError(`The enum value '${type.name}.${enumVal.name}' is deprecated.${reason ? ' ' + reason : ''}`, [node])); } } }, })); return errors; } function removeFieldIfDirectives(node, directiveNames) { if (node.directives?.some(d => directiveNames.includes(d.name.value))) { return null; } return node; } function removeDirectives(node, directiveNames) { if (node.directives) { return { ...node, directives: node.directives.filter(d => !directiveNames.includes(d.name.value)), }; } return node; } function getReachableTypes(schema) { const reachableTypes = new Set(); const collect = (type) => { const typeName = type.name; if (reachableTypes.has(typeName)) { return; } reachableTypes.add(typeName); if ((0, graphql_1.isScalarType)(type)) { return; } if ((0, graphql_1.isInterfaceType)(type) || (0, graphql_1.isObjectType)(type)) { if ((0, graphql_1.isInterfaceType)(type)) { const { objects, interfaces } = schema.getImplementations(type); for (const child of objects) { collect(child); } for (const child of interfaces) { collect(child); } } const fields = type.getFields(); for (const fieldName in fields) { const field = fields[fieldName]; collect(resolveOutputType(field.type)); const args = field.args; for (const argName in args) { const arg = args[argName]; collect(resolveInputType(arg.type)); } } } else if ((0, graphql_1.isUnionType)(type)) { const types = type.getTypes(); for (const child of types) { collect(child); } } else if ((0, graphql_1.isInputObjectType)(type)) { const fields = type.getFields(); for (const fieldName in fields) { const field = fields[fieldName]; collect(resolveInputType(field.type)); } } }; for (const type of [ schema.getQueryType(), schema.getMutationType(), schema.getSubscriptionType(), ]) { if (type) { collect(type); } } return reachableTypes; } function resolveOutputType(output) { if ((0, graphql_1.isListType)(output) || (0, graphql_1.isNonNullType)(output)) { return resolveOutputType(output.ofType); } return output; } function resolveInputType(input) { if ((0, graphql_1.isListType)(input) || (0, graphql_1.isNonNullType)(input)) { return resolveInputType(input.ofType); } return input; }