UNPKG

@graphql-eslint/eslint-plugin

Version:
163 lines (156 loc) 6.32 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.rule = void 0; const graphql_1 = require("graphql"); const utils_js_1 = require("../utils.js"); const RULE_ID = 'strict-id-in-types'; const schema = { type: 'array', maxItems: 1, items: { type: 'object', additionalProperties: false, properties: { acceptedIdNames: { ...utils_js_1.ARRAY_DEFAULT_OPTIONS, default: ['id'], }, acceptedIdTypes: { ...utils_js_1.ARRAY_DEFAULT_OPTIONS, default: ['ID'], }, exceptions: { type: 'object', additionalProperties: false, properties: { types: { ...utils_js_1.ARRAY_DEFAULT_OPTIONS, description: 'This is used to exclude types with names that match one of the specified values.', }, suffixes: { ...utils_js_1.ARRAY_DEFAULT_OPTIONS, description: 'This is used to exclude types with names with suffixes that match one of the specified values.', }, }, }, }, }, }; exports.rule = { meta: { type: 'suggestion', docs: { description: 'Requires output types to have one unique identifier unless they do not have a logical one. Exceptions can be used to ignore output types that do not have unique identifiers.', category: 'Schema', recommended: true, url: `https://github.com/B2o5T/graphql-eslint/blob/master/docs/rules/${RULE_ID}.md`, requiresSchema: true, examples: [ { title: 'Incorrect', usage: [ { acceptedIdNames: ['id', '_id'], acceptedIdTypes: ['ID'], exceptions: { suffixes: ['Payload'] }, }, ], code: /* GraphQL */ ` # Incorrect field name type InvalidFieldName { key: ID! } # Incorrect field type type InvalidFieldType { id: String! } # Incorrect exception suffix type InvalidSuffixResult { data: String! } # Too many unique identifiers. Must only contain one. type InvalidFieldName { id: ID! _id: ID! } `, }, { title: 'Correct', usage: [ { acceptedIdNames: ['id', '_id'], acceptedIdTypes: ['ID'], exceptions: { types: ['Error'], suffixes: ['Payload'] }, }, ], code: /* GraphQL */ ` type User { id: ID! } type Post { _id: ID! } type CreateUserPayload { data: String! } type Error { message: String! } `, }, ], }, schema, }, create(context) { const options = { acceptedIdNames: ['id'], acceptedIdTypes: ['ID'], exceptions: {}, ...context.options[0], }; const schema = (0, utils_js_1.requireGraphQLSchemaFromContext)(RULE_ID, context); const rootTypeNames = [ schema.getQueryType(), schema.getMutationType(), schema.getSubscriptionType(), ] .filter(utils_js_1.truthy) .map(type => type.name); const selector = `ObjectTypeDefinition[name.value!=/^(${rootTypeNames.join('|')})$/]`; return { [selector](node) { var _a, _b, _c; const typeName = node.name.value; const shouldIgnoreNode = ((_a = options.exceptions.types) === null || _a === void 0 ? void 0 : _a.includes(typeName)) || ((_b = options.exceptions.suffixes) === null || _b === void 0 ? void 0 : _b.some(suffix => typeName.endsWith(suffix))); if (shouldIgnoreNode) { return; } const validIds = (_c = node.fields) === null || _c === void 0 ? void 0 : _c.filter(field => { const fieldNode = field.rawNode(); const isValidIdName = options.acceptedIdNames.includes(fieldNode.name.value); // To be a valid type, it must be non-null and one of the accepted types. let isValidIdType = false; if (fieldNode.type.kind === graphql_1.Kind.NON_NULL_TYPE && fieldNode.type.type.kind === graphql_1.Kind.NAMED_TYPE) { isValidIdType = options.acceptedIdTypes.includes(fieldNode.type.type.name.value); } return isValidIdName && isValidIdType; }); // Usually, there should be only one unique identifier field per type. // Some clients allow multiple fields to be used. If more people need this, // we can extend this rule later. if ((validIds === null || validIds === void 0 ? void 0 : validIds.length) !== 1) { const pluralNamesSuffix = options.acceptedIdNames.length > 1 ? 's' : ''; const pluralTypesSuffix = options.acceptedIdTypes.length > 1 ? 's' : ''; context.report({ node: node.name, message: `${typeName} must have exactly one non-nullable unique identifier. Accepted name${pluralNamesSuffix}: ${(0, utils_js_1.englishJoinWords)(options.acceptedIdNames)}. Accepted type${pluralTypesSuffix}: ${(0, utils_js_1.englishJoinWords)(options.acceptedIdTypes)}.`, }); } }, }; }, };