@graphql-inspector/cli
Version:
Tooling for GraphQL. Compare GraphQL Schemas, check documents, find breaking changes, find similar types.
139 lines (138 loc) • 7.45 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.diffSchema = diffSchema;
const graphql_1 = require("graphql");
const compare_js_1 = require("../utils/compare.js");
const graphql_js_1 = require("../utils/graphql.js");
const directive_usage_js_1 = require("./changes/directive-usage.js");
const directive_js_1 = require("./changes/directive.js");
const schema_js_1 = require("./changes/schema.js");
const type_js_1 = require("./changes/type.js");
const directive_js_2 = require("./directive.js");
const enum_js_1 = require("./enum.js");
const input_js_1 = require("./input.js");
const interface_js_1 = require("./interface.js");
const object_js_1 = require("./object.js");
const scalar_js_1 = require("./scalar.js");
const union_js_1 = require("./union.js");
function diffSchema(oldSchema, newSchema) {
const changes = [];
function addChange(change) {
changes.push(change);
}
// important for directives to come first so that any directive usages that come
// later are able to find the directive definitions that were added.
(0, compare_js_1.compareLists)((oldSchema?.getDirectives() ?? []).filter(t => !(0, graphql_1.isSpecifiedDirective)(t)), (newSchema?.getDirectives() ?? []).filter(t => !(0, graphql_1.isSpecifiedDirective)(t)), {
onAdded(directive) {
addChange((0, directive_js_1.directiveAdded)(directive));
(0, directive_js_2.changesInDirective)(null, directive, addChange);
},
onRemoved(directive) {
addChange((0, directive_js_1.directiveRemoved)(directive));
},
onMutual(directive) {
(0, directive_js_2.changesInDirective)(directive.oldVersion, directive.newVersion, addChange);
},
});
changesInSchema(oldSchema, newSchema, addChange);
(0, compare_js_1.compareLists)(Object.values(oldSchema?.getTypeMap() ?? {}).filter(t => !(0, graphql_js_1.isPrimitive)(t) && !(0, graphql_js_1.isForIntrospection)(t)), Object.values(newSchema?.getTypeMap() ?? {}).filter(t => !(0, graphql_js_1.isPrimitive)(t) && !(0, graphql_js_1.isForIntrospection)(t)), {
onAdded(type) {
addChange((0, type_js_1.typeAdded)(type));
changesInType(null, type, addChange);
},
onRemoved(type) {
addChange((0, type_js_1.typeRemoved)(type));
},
onMutual(type) {
changesInType(type.oldVersion, type.newVersion, addChange);
},
});
(0, compare_js_1.compareDirectiveLists)(oldSchema?.astNode?.directives || [], newSchema?.astNode?.directives || [], {
onAdded(directive) {
addChange((0, directive_usage_js_1.directiveUsageAdded)(graphql_1.Kind.SCHEMA_DEFINITION, directive, newSchema, false));
(0, directive_usage_js_1.directiveUsageChanged)(null, directive, addChange);
},
onMutual(directive) {
(0, directive_usage_js_1.directiveUsageChanged)(directive.oldVersion, directive.newVersion, addChange);
},
onRemoved(directive) {
addChange((0, directive_usage_js_1.directiveUsageRemoved)(graphql_1.Kind.SCHEMA_DEFINITION, directive, oldSchema));
},
});
/**
* Extracting directives from the schema definition requires using the AST. But the AST does not combine the
* definition and extensions like the GraphQL Type System. Therefore, the extensions must be looped over as well.
**/
for (let e = 0; e <
Math.max(oldSchema?.extensionASTNodes.length ?? 0, newSchema?.extensionASTNodes.length ?? 0); e++) {
(0, compare_js_1.compareDirectiveLists)(oldSchema?.extensionASTNodes[e]?.directives || [], newSchema?.extensionASTNodes[e]?.directives || [], {
onAdded(directive) {
addChange((0, directive_usage_js_1.directiveUsageAdded)(graphql_1.Kind.SCHEMA_DEFINITION, directive, newSchema, false));
(0, directive_usage_js_1.directiveUsageChanged)(null, directive, addChange);
},
onMutual(directive) {
(0, directive_usage_js_1.directiveUsageChanged)(directive.oldVersion, directive.newVersion, addChange);
},
onRemoved(directive) {
addChange((0, directive_usage_js_1.directiveUsageRemoved)(graphql_1.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 ((0, compare_js_1.isNotEqual)(oldRoot.query, newRoot.query)) {
addChange((0, schema_js_1.schemaQueryTypeChanged)(oldSchema, newSchema));
}
if ((0, compare_js_1.isNotEqual)(oldRoot.mutation, newRoot.mutation)) {
addChange((0, schema_js_1.schemaMutationTypeChanged)(oldSchema, newSchema));
}
if ((0, compare_js_1.isNotEqual)(oldRoot.subscription, newRoot.subscription)) {
addChange((0, schema_js_1.schemaSubscriptionTypeChanged)(oldSchema, newSchema));
}
}
function changesInType(oldType, newType, addChange) {
if (((0, compare_js_1.isVoid)(oldType) || (0, graphql_1.isEnumType)(oldType)) && (0, graphql_1.isEnumType)(newType)) {
(0, enum_js_1.changesInEnum)(oldType, newType, addChange);
}
else if (((0, compare_js_1.isVoid)(oldType) || (0, graphql_1.isUnionType)(oldType)) && (0, graphql_1.isUnionType)(newType)) {
(0, union_js_1.changesInUnion)(oldType, newType, addChange);
}
else if (((0, compare_js_1.isVoid)(oldType) || (0, graphql_1.isInputObjectType)(oldType)) && (0, graphql_1.isInputObjectType)(newType)) {
(0, input_js_1.changesInInputObject)(oldType, newType, addChange);
}
else if (((0, compare_js_1.isVoid)(oldType) || (0, graphql_1.isObjectType)(oldType)) && (0, graphql_1.isObjectType)(newType)) {
(0, object_js_1.changesInObject)(oldType, newType, addChange);
}
else if (((0, compare_js_1.isVoid)(oldType) || (0, graphql_1.isInterfaceType)(oldType)) && (0, graphql_1.isInterfaceType)(newType)) {
(0, interface_js_1.changesInInterface)(oldType, newType, addChange);
}
else if (((0, compare_js_1.isVoid)(oldType) || (0, graphql_1.isScalarType)(oldType)) && (0, graphql_1.isScalarType)(newType)) {
(0, scalar_js_1.changesInScalar)(oldType, newType, addChange);
}
else if (!(0, compare_js_1.isVoid)(oldType)) {
// no need to call if oldType is void since the type will be captured by the TypeAdded change.
addChange((0, type_js_1.typeKindChanged)(oldType, newType));
}
if ((0, compare_js_1.isNotEqual)(oldType?.description, newType.description)) {
if ((0, compare_js_1.isVoid)(oldType?.description)) {
addChange((0, type_js_1.typeDescriptionAdded)(newType));
}
else if (oldType.description && (0, compare_js_1.isVoid)(newType.description)) {
addChange((0, type_js_1.typeDescriptionRemoved)(oldType));
}
else {
addChange((0, type_js_1.typeDescriptionChanged)(oldType, newType));
}
}
}