UNPKG

@graphql-eslint/eslint-plugin

Version:
108 lines (107 loc) 5.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.rule = exports.NON_OBJECT_TYPES = void 0; const graphql_1 = require("graphql"); const MUST_BE_OBJECT_TYPE = 'MUST_BE_OBJECT_TYPE'; const MUST_CONTAIN_FIELD_EDGES = 'MUST_CONTAIN_FIELD_EDGES'; const MUST_CONTAIN_FIELD_PAGE_INFO = 'MUST_CONTAIN_FIELD_PAGE_INFO'; const MUST_HAVE_CONNECTION_SUFFIX = 'MUST_HAVE_CONNECTION_SUFFIX'; const EDGES_FIELD_MUST_RETURN_LIST_TYPE = 'EDGES_FIELD_MUST_RETURN_LIST_TYPE'; const PAGE_INFO_FIELD_MUST_RETURN_NON_NULL_TYPE = 'PAGE_INFO_FIELD_MUST_RETURN_NON_NULL_TYPE'; exports.NON_OBJECT_TYPES = [ graphql_1.Kind.SCALAR_TYPE_DEFINITION, graphql_1.Kind.UNION_TYPE_DEFINITION, graphql_1.Kind.UNION_TYPE_EXTENSION, graphql_1.Kind.INPUT_OBJECT_TYPE_DEFINITION, graphql_1.Kind.INPUT_OBJECT_TYPE_EXTENSION, graphql_1.Kind.ENUM_TYPE_DEFINITION, graphql_1.Kind.ENUM_TYPE_EXTENSION, graphql_1.Kind.INTERFACE_TYPE_DEFINITION, graphql_1.Kind.INTERFACE_TYPE_EXTENSION, ]; const notConnectionTypesSelector = `:matches(${exports.NON_OBJECT_TYPES})[name.value=/Connection$/] > .name`; const hasEdgesField = (node) => node.fields.some(field => field.name.value === 'edges'); const hasPageInfoField = (node) => node.fields.some(field => field.name.value === 'pageInfo'); exports.rule = { meta: { type: 'problem', docs: { category: 'Schema', description: [ 'Set of rules to follow Relay specification for Connection types.', '', '- Any type whose name ends in "Connection" is considered by spec to be a `Connection type`', '- Connection type must be an Object type', '- Connection type must contain a field `edges` that return a list type that wraps an edge type', '- Connection type must contain a field `pageInfo` that return a non-null `PageInfo` Object type', ].join('\n'), url: 'https://github.com/B2o5T/graphql-eslint/blob/master/docs/rules/relay-connection-types.md', isDisabledForAllConfig: true, examples: [ { title: 'Incorrect', code: /* GraphQL */ ` type UserPayload { # should be an Object type with \`Connection\` suffix edges: UserEdge! # should return a list type pageInfo: PageInfo # should return a non-null \`PageInfo\` Object type } `, }, { title: 'Correct', code: /* GraphQL */ ` type UserConnection { edges: [UserEdge] pageInfo: PageInfo! } `, }, ], }, messages: { // Connection types [MUST_BE_OBJECT_TYPE]: 'Connection type must be an Object type.', [MUST_HAVE_CONNECTION_SUFFIX]: 'Connection type must have `Connection` suffix.', [MUST_CONTAIN_FIELD_EDGES]: 'Connection type must contain a field `edges` that return a list type.', [MUST_CONTAIN_FIELD_PAGE_INFO]: 'Connection type must contain a field `pageInfo` that return a non-null `PageInfo` Object type.', [EDGES_FIELD_MUST_RETURN_LIST_TYPE]: '`edges` field must return a list type.', [PAGE_INFO_FIELD_MUST_RETURN_NON_NULL_TYPE]: '`pageInfo` field must return a non-null `PageInfo` Object type.', }, schema: [], }, create(context) { return { [notConnectionTypesSelector](node) { context.report({ node, messageId: MUST_BE_OBJECT_TYPE }); }, ':matches(ObjectTypeDefinition, ObjectTypeExtension)[name.value!=/Connection$/]'(node) { if (hasEdgesField(node) && hasPageInfoField(node)) { context.report({ node: node.name, messageId: MUST_HAVE_CONNECTION_SUFFIX }); } }, ':matches(ObjectTypeDefinition, ObjectTypeExtension)[name.value=/Connection$/]'(node) { if (!hasEdgesField(node)) { context.report({ node: node.name, messageId: MUST_CONTAIN_FIELD_EDGES }); } if (!hasPageInfoField(node)) { context.report({ node: node.name, messageId: MUST_CONTAIN_FIELD_PAGE_INFO }); } }, ':matches(ObjectTypeDefinition, ObjectTypeExtension)[name.value=/Connection$/] > FieldDefinition[name.value=edges] > .gqlType'(node) { const isListType = node.kind === graphql_1.Kind.LIST_TYPE || (node.kind === graphql_1.Kind.NON_NULL_TYPE && node.gqlType.kind === graphql_1.Kind.LIST_TYPE); if (!isListType) { context.report({ node, messageId: EDGES_FIELD_MUST_RETURN_LIST_TYPE }); } }, ':matches(ObjectTypeDefinition, ObjectTypeExtension)[name.value=/Connection$/] > FieldDefinition[name.value=pageInfo] > .gqlType'(node) { const isNonNullPageInfoType = node.kind === graphql_1.Kind.NON_NULL_TYPE && node.gqlType.kind === graphql_1.Kind.NAMED_TYPE && node.gqlType.name.value === 'PageInfo'; if (!isNonNullPageInfoType) { context.report({ node, messageId: PAGE_INFO_FIELD_MUST_RETURN_NON_NULL_TYPE }); } }, }; }, };