@graphql-inspector/cli
Version:
Tooling for GraphQL. Compare GraphQL Schemas, check documents, find breaking changes, find similar types.
115 lines (114 loc) • 5.2 kB
JavaScript
import { isEnumType, isInputObjectType, isInterfaceType, isObjectType, isScalarType, isUnionType, Kind, } from 'graphql';
import { compareDirectiveLists, compareLists, isNotEqual, isVoid } from '../utils/compare.js';
import { isPrimitive } from '../utils/graphql.js';
import { directiveUsageAdded, directiveUsageChanged, directiveUsageRemoved, } from './changes/directive-usage.js';
import { directiveAdded, directiveRemoved } from './changes/directive.js';
import { schemaMutationTypeChanged, schemaQueryTypeChanged, schemaSubscriptionTypeChanged, } from './changes/schema.js';
import { typeAdded, typeDescriptionAdded, typeDescriptionChanged, typeDescriptionRemoved, typeKindChanged, typeRemoved, } from './changes/type.js';
import { changesInDirective } from './directive.js';
import { changesInEnum } from './enum.js';
import { changesInInputObject } from './input.js';
import { changesInInterface } from './interface.js';
import { changesInObject } from './object.js';
import { changesInScalar } from './scalar.js';
import { changesInUnion } from './union.js';
export function diffSchema(oldSchema, newSchema) {
const changes = [];
function addChange(change) {
changes.push(change);
}
changesInSchema(oldSchema, newSchema, addChange);
compareLists(Object.values(oldSchema.getTypeMap()).filter(t => !isPrimitive(t)), Object.values(newSchema.getTypeMap()).filter(t => !isPrimitive(t)), {
onAdded(type) {
addChange(typeAdded(type));
changesInType(null, type, addChange);
},
onRemoved(type) {
addChange(typeRemoved(type));
},
onMutual(type) {
changesInType(type.oldVersion, type.newVersion, addChange);
},
});
compareLists(oldSchema.getDirectives(), newSchema.getDirectives(), {
onAdded(directive) {
addChange(directiveAdded(directive));
changesInDirective(null, directive, addChange);
},
onRemoved(directive) {
addChange(directiveRemoved(directive));
},
onMutual(directive) {
changesInDirective(directive.oldVersion, directive.newVersion, addChange);
},
});
compareDirectiveLists(oldSchema.astNode?.directives || [], newSchema.astNode?.directives || [], {
onAdded(directive) {
addChange(directiveUsageAdded(Kind.SCHEMA_DEFINITION, directive, newSchema, false));
directiveUsageChanged(null, directive, addChange);
},
onMutual(directive) {
directiveUsageChanged(directive.oldVersion, directive.newVersion, addChange);
},
onRemoved(directive) {
addChange(directiveUsageRemoved(Kind.SCHEMA_DEFINITION, directive, oldSchema));
},
});
return changes;
}
function changesInSchema(oldSchema, newSchema, addChange) {
const oldRoot = {
query: (oldSchema.getQueryType() || {}).name,
mutation: (oldSchema.getMutationType() || {}).name,
subscription: (oldSchema.getSubscriptionType() || {}).name,
};
const newRoot = {
query: (newSchema.getQueryType() || {}).name,
mutation: (newSchema.getMutationType() || {}).name,
subscription: (newSchema.getSubscriptionType() || {}).name,
};
if (isNotEqual(oldRoot.query, newRoot.query)) {
addChange(schemaQueryTypeChanged(oldSchema, newSchema));
}
if (isNotEqual(oldRoot.mutation, newRoot.mutation)) {
addChange(schemaMutationTypeChanged(oldSchema, newSchema));
}
if (isNotEqual(oldRoot.subscription, newRoot.subscription)) {
addChange(schemaSubscriptionTypeChanged(oldSchema, newSchema));
}
}
function changesInType(oldType, newType, addChange) {
if ((isVoid(oldType) || isEnumType(oldType)) && isEnumType(newType)) {
changesInEnum(oldType, newType, addChange);
}
else if ((isVoid(oldType) || isUnionType(oldType)) && isUnionType(newType)) {
changesInUnion(oldType, newType, addChange);
}
else if ((isVoid(oldType) || isInputObjectType(oldType)) && isInputObjectType(newType)) {
changesInInputObject(oldType, newType, addChange);
}
else if ((isVoid(oldType) || isObjectType(oldType)) && isObjectType(newType)) {
changesInObject(oldType, newType, addChange);
}
else if ((isVoid(oldType) || isInterfaceType(oldType)) && isInterfaceType(newType)) {
changesInInterface(oldType, newType, addChange);
}
else if ((isVoid(oldType) || isScalarType(oldType)) && isScalarType(newType)) {
changesInScalar(oldType, newType, addChange);
}
else if (!isVoid(oldType)) {
// no need to call if oldType is void since the type will be captured by the TypeAdded change.
addChange(typeKindChanged(oldType, newType));
}
if (isNotEqual(oldType?.description, newType.description)) {
if (isVoid(oldType?.description)) {
addChange(typeDescriptionAdded(newType));
}
else if (oldType.description && isVoid(newType.description)) {
addChange(typeDescriptionRemoved(oldType));
}
else {
addChange(typeDescriptionChanged(oldType, newType));
}
}
}