graphql

Search for an npm package
'use strict';
Object.defineProperty(exports, '__esModule', {
value: true,
});
exports.FieldsOnCorrectTypeRule = FieldsOnCorrectTypeRule;
var _didYouMean = require('../../jsutils/didYouMean.js');
var _naturalCompare = require('../../jsutils/naturalCompare.js');
var _suggestionList = require('../../jsutils/suggestionList.js');
var _GraphQLError = require('../../error/GraphQLError.js');
var _definition = require('../../type/definition.js');
/**
* Fields on correct type
*
* A GraphQL document is only valid if all fields selected are defined by the
* parent type, or are an allowed meta field such as __typename.
*
* See https://spec.graphql.org/draft/#sec-Field-Selections
*/
function FieldsOnCorrectTypeRule(context) {
return {
Field(node) {
const type = context.getParentType();
if (type) {
const fieldDef = context.getFieldDef();
if (!fieldDef) {
// This field doesn't exist, lets look for suggestions.
const schema = context.getSchema();
const fieldName = node.name.value; // First determine if there are any suggested types to condition on.
let suggestion = (0, _didYouMean.didYouMean)(
'to use an inline fragment on',
getSuggestedTypeNames(schema, type, fieldName),
); // If there are no suggested types, then perhaps this was a typo?
if (suggestion === '') {
suggestion = (0, _didYouMean.didYouMean)(
getSuggestedFieldNames(type, fieldName),
);
} // Report an error, including helpful suggestions.
context.reportError(
new _GraphQLError.GraphQLError(
`Cannot query field "${fieldName}" on type "${type.name}".` +
suggestion,
{
nodes: node,
},
),
);
}
}
},
};
}
/**
* Go through all of the implementations of type, as well as the interfaces that
* they implement. If any of those types include the provided field, suggest them,
* sorted by how often the type is referenced.
*/
function getSuggestedTypeNames(schema, type, fieldName) {
if (!(0, _definition.isAbstractType)(type)) {
// Must be an Object type, which does not have possible fields.
return [];
}
const suggestedTypes = new Set();
const usageCount = Object.create(null);
for (const possibleType of schema.getPossibleTypes(type)) {
if (!possibleType.getFields()[fieldName]) {
continue;
} // This object type defines this field.
suggestedTypes.add(possibleType);
usageCount[possibleType.name] = 1;
for (const possibleInterface of possibleType.getInterfaces()) {
var _usageCount$possibleI;
if (!possibleInterface.getFields()[fieldName]) {
continue;
} // This interface type defines this field.
suggestedTypes.add(possibleInterface);
usageCount[possibleInterface.name] =
((_usageCount$possibleI = usageCount[possibleInterface.name]) !==
null && _usageCount$possibleI !== void 0
? _usageCount$possibleI
: 0) + 1;
}
}
return [...suggestedTypes]
.sort((typeA, typeB) => {
// Suggest both interface and object types based on how common they are.
const usageCountDiff = usageCount[typeB.name] - usageCount[typeA.name];
if (usageCountDiff !== 0) {
return usageCountDiff;
} // Suggest super types first followed by subtypes
if (
(0, _definition.isInterfaceType)(typeA) &&
schema.isSubType(typeA, typeB)
) {
return -1;
}
if (
(0, _definition.isInterfaceType)(typeB) &&
schema.isSubType(typeB, typeA)
) {
return 1;
}
return (0, _naturalCompare.naturalCompare)(typeA.name, typeB.name);
})
.map((x) => x.name);
}
/**
* For the field name provided, determine if there are any similar field names
* that may be the result of a typo.
*/
function getSuggestedFieldNames(type, fieldName) {
if (
(0, _definition.isObjectType)(type) ||
(0, _definition.isInterfaceType)(type)
) {
const possibleFieldNames = Object.keys(type.getFields());
return (0, _suggestionList.suggestionList)(fieldName, possibleFieldNames);
} // Otherwise, must be a Union type, which does not define fields.
return [];
}