Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 20s
Build & Deploy / 🧪 QA (push) Failing after 34s
Build & Deploy / 🏗️ Build (push) Has started running
Build & Deploy / 🚀 Deploy (push) Has been cancelled
Build & Deploy / 🧪 Smoke Test (push) Has been cancelled
Build & Deploy / ⚡ Lighthouse (push) Has been cancelled
Build & Deploy / 🔔 Notify (push) Has been cancelled
146 lines
4.3 KiB
Plaintext
146 lines
4.3 KiB
Plaintext
'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 [];
|
|
}
|