UNPKG

@maktouch/graphql-directive-connection

Version:

Generate relay connections by marking fields with a @connection directive.

166 lines (165 loc) 6.78 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const schema_1 = require("@graphql-tools/schema"); const utils_1 = require("@graphql-tools/utils"); const wrap_1 = require("@graphql-tools/wrap"); const graphql_1 = require("graphql"); function connectionDirective(directiveName, options) { return { connectionDirectiveTypeDefs: `directive @${directiveName} on FIELD_DEFINITION`, connectionDirectiveTransform: (schema) => { const newTypeDefs = []; const foundTypes = {}; const connectionTypes = {}; const markedLocations = {}; // variables for cacheControl: const connectionTypeGreatestMaxAge = {}; // Perform visitations: const fieldVisitor = (fieldConfig, fieldName, typeName) => { var _a; const directives = utils_1.getDirectives(schema, fieldConfig); for (const directive of directives) { if (directive.name === directiveName) { const baseName = getBaseType(fieldConfig.type.toString()); connectionTypes[baseName] = true; markedLocations[`${typeName}.${fieldName}`] = baseName + 'Connection'; // fieldConfig.type = makeConnectionType(fieldConfig.type) // does not work // return fieldConfig } if (directive.name === 'cacheControl') { const maxAge = (_a = directive.args) === null || _a === void 0 ? void 0 : _a.maxAge; if (typeof maxAge === 'number') { const baseName = getBaseType(fieldConfig.type.toString()); if (!connectionTypeGreatestMaxAge.hasOwnProperty(baseName) || maxAge > connectionTypeGreatestMaxAge[baseName]) { connectionTypeGreatestMaxAge[baseName] = maxAge; } } } } return undefined; }; utils_1.mapSchema(schema, { [utils_1.MapperKind.TYPE]: (type) => { foundTypes[type.name] = type; return undefined; }, [utils_1.MapperKind.INTERFACE_FIELD]: fieldVisitor, [utils_1.MapperKind.OBJECT_FIELD]: fieldVisitor, }); // Construct new types: if (!foundTypes['PageInfo']) { newTypeDefs.push(` type PageInfo { hasNextPage: Boolean! hasPreviousPage: Boolean! startCursor: String endCursor: String } `); } for (const name of Object.keys(connectionTypes)) { // This applies the cacheControl to Edge type and edges, pageInfo fields // The cacheControl is not applied to a Connection and Node types // to comply with GraphQL List cacheControl behavior which has disabled cache by default const maxAge = connectionTypeGreatestMaxAge[name]; const needsCacheControl = (options === null || options === void 0 ? void 0 : options.useCacheControl) && typeof maxAge === 'number'; const cacheControl = needsCacheControl ? ` @cacheControl(maxAge: ${maxAge})` : ''; const newEdgeName = `${name}Edge`; if (!foundTypes[newEdgeName]) { newTypeDefs.push(` type ${newEdgeName}${cacheControl} { cursor: String! node: ${name} } `); } const newConnectionName = `${name}Connection`; if (!foundTypes[newConnectionName]) { newTypeDefs.push(` type ${newConnectionName} { totalCount: Int! edges: [${newEdgeName}]${cacheControl} pageInfo: PageInfo!${cacheControl} } `); } } schema = schema_1.mergeSchemas({ schemas: [schema], typeDefs: newTypeDefs, }); // Rename field types. const transformer = (typeName, fieldName, fieldConfig) => { var _a, _b; const mark = markedLocations[`${typeName}.${fieldName}`]; if (mark) { fieldConfig.type = makeConnectionType(fieldConfig.type); fieldConfig.args = Object.assign(Object.assign({}, fieldConfig.args), makeConnectionArgs()); const remainingDirectives = (_b = (_a = fieldConfig === null || fieldConfig === void 0 ? void 0 : fieldConfig.astNode) === null || _a === void 0 ? void 0 : _a.directives) === null || _b === void 0 ? void 0 : _b.filter((dir) => dir.name.value !== directiveName); fieldConfig.astNode = Object.assign(Object.assign({}, fieldConfig.astNode), { directives: remainingDirectives }); return fieldConfig; } else return undefined; }; schema = wrap_1.wrapSchema({ schema, transforms: [ new wrap_1.TransformInterfaceFields(transformer), new wrap_1.TransformObjectFields(transformer), ], }); return schema; }, }; } exports.default = connectionDirective; function getBaseType(type) { if (!type) return ''; if (typeof type !== 'string') return ''; return type .replace(/:/g, '') .replace(/\[/g, '') .replace(/\]/g, '') .replace(/!/g, '') .replace(/@/g, '') .trim(); } function makeConnectionType(type) { const formattedType = type.toString(); const baseName = getBaseType(formattedType); return new graphql_1.GraphQLObjectType({ name: `${baseName}Connection`, fields: {}, }); } function makeConnectionArgs() { return { after: { type: new graphql_1.GraphQLScalarType({ name: 'String', }), }, first: { type: new graphql_1.GraphQLScalarType({ name: 'Int', }), }, before: { type: new graphql_1.GraphQLScalarType({ name: 'String', }), }, last: { type: new graphql_1.GraphQLScalarType({ name: 'Int', }), }, }; }