UNPKG

graphql-modules

Version:

Create reusable, maintainable, testable and extendable GraphQL modules

271 lines (270 loc) 10.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.mockModule = mockModule; exports.testModule = testModule; const graphql_1 = require("graphql"); const factory_1 = require("../module/factory"); const application_1 = require("../application/application"); const module_1 = require("../module/module"); function mockModule(testedModule, overrideConfig) { const sourceProviders = typeof testedModule.config.providers === 'function' ? testedModule.config.providers() : testedModule.config.providers; const overrideProviders = typeof overrideConfig.providers === 'function' ? overrideConfig.providers() : overrideConfig.providers; const newModule = (0, module_1.createModule)({ ...testedModule.config, providers: [...(sourceProviders || []), ...(overrideProviders || [])], }); newModule['ɵoriginalModule'] = testedModule; return newModule; } function testModule(testedModule, config) { var _a; const mod = transformModule(testedModule, config); const modules = [mod].concat((_a = config === null || config === void 0 ? void 0 : config.modules) !== null && _a !== void 0 ? _a : []); return (0, application_1.createApplication)({ ...(config || {}), modules, providers: config === null || config === void 0 ? void 0 : config.providers, middlewares: config === null || config === void 0 ? void 0 : config.middlewares, }); } function transformModule(mod, config) { const transforms = []; if (config === null || config === void 0 ? void 0 : config.replaceExtensions) { transforms.push((m) => (0, factory_1.moduleFactory)({ ...m.config, typeDefs: replaceExtensions(m.typeDefs), })); } if (config === null || config === void 0 ? void 0 : config.typeDefs) { transforms.push((m) => (0, factory_1.moduleFactory)({ ...m.config, typeDefs: m.typeDefs.concat(config.typeDefs), })); } if (config === null || config === void 0 ? void 0 : config.inheritTypeDefs) { transforms.push((m) => (0, factory_1.moduleFactory)({ ...m.config, typeDefs: inheritTypeDefs(m.typeDefs, config.inheritTypeDefs), })); } if (config === null || config === void 0 ? void 0 : config.resolvers) { transforms.push((m) => { const resolvers = m.config.resolvers ? Array.isArray(m.config.resolvers) ? m.config.resolvers : [m.config.resolvers] : []; return (0, factory_1.moduleFactory)({ ...m.config, resolvers: resolvers.concat(config.resolvers), }); }); } if (config === null || config === void 0 ? void 0 : config.providers) { transforms.push((m) => { const sourceProviders = typeof m.config.providers === 'function' ? m.config.providers() : m.config.providers; const overrideProviders = typeof config.providers === 'function' ? config.providers() : config.providers; return (0, factory_1.moduleFactory)({ ...m.config, providers: [...(sourceProviders || []), ...(overrideProviders || [])], }); }); } if (transforms) { return transforms.reduce((m, transform) => transform(m), mod); } return mod; } function inheritTypeDefs(originalTypeDefs, modules) { const original = (0, graphql_1.concatAST)(originalTypeDefs); const typeDefs = treeshakeTypesDefs(original, modules.reduce((typeDefs, externalMod) => typeDefs.concat(externalMod.typeDefs), [])); return typeDefs; } function replaceExtensions(typeDefs) { const types = []; const extensions = []; // List all object types typeDefs.forEach((doc) => { (0, graphql_1.visit)(doc, { ObjectTypeDefinition(node) { types.push(node.name.value); }, }); }); // turn object type extensions into object types return typeDefs.map((doc) => { return (0, graphql_1.visit)(doc, { ObjectTypeExtension(node) { // only if object type doesn't exist if (extensions.includes(node.name.value) || types.includes(node.name.value)) { return node; } return { ...node, kind: graphql_1.Kind.OBJECT_TYPE_DEFINITION, }; }, }); }); } function treeshakeTypesDefs(originalSource, sources) { const namedTypes = originalSource.definitions.filter(isNamedTypeDefinition); const typesToVisit = namedTypes.map((def) => def.name.value); const rootFields = namedTypes.reduce((acc, node) => { const typeName = node.name.value; if (isRootType(typeName) && hasFields(node)) { if (!acc[typeName]) { acc[typeName] = []; } node.fields.forEach((field) => { acc[typeName].push(field.name.value); }); } return acc; }, {}); const schema = (0, graphql_1.concatAST)([originalSource].concat(sources)); const involvedTypes = new Set(visitTypes(schema, typesToVisit, rootFields)); return { kind: graphql_1.Kind.DOCUMENT, definitions: schema.definitions.filter((def) => { var _a, _b; if (isNamedTypeDefinition(def)) { const typeName = def.name.value; if (!involvedTypes.has(def.name.value)) { return false; } if ((_a = rootFields[typeName]) === null || _a === void 0 ? void 0 : _a.length) { const rootType = def; if ((_b = rootType.fields) === null || _b === void 0 ? void 0 : _b.every((field) => !rootFields[typeName].includes(field.name.value))) { return false; } } } return true; }), }; } function isNamedTypeDefinition(def) { return (!!def && def.kind !== graphql_1.Kind.SCHEMA_DEFINITION && def.kind !== graphql_1.Kind.SCHEMA_EXTENSION); } function visitTypes(schema, types, rootFields) { const visitedTypes = []; const scalars = schema.definitions .filter((def) => def.kind === graphql_1.Kind.SCALAR_TYPE_DEFINITION || def.kind === graphql_1.Kind.SCALAR_TYPE_EXTENSION) .map((def) => def.name.value); for (const typeName of types) { collectType(typeName); } return visitedTypes; function collectField(field, parentTypeName) { var _a; if (parentTypeName && isRootType(parentTypeName) && ((_a = rootFields[parentTypeName]) === null || _a === void 0 ? void 0 : _a.length) && !rootFields[parentTypeName].includes(field.name.value)) { return; } collectType(resolveType(field.type)); if (field.arguments) { field.arguments.forEach((arg) => { collectType(resolveType(arg.type)); }); } if (field.directives) { field.directives.forEach((directive) => { collectType(directive.name.value); }); } } function collectType(typeName) { if (visitedTypes.includes(typeName)) { return; } if (isScalar(typeName)) { visitedTypes.push(typeName); return; } const types = findTypes(typeName); visitedTypes.push(typeName); types.forEach((type) => { if (hasFields(type)) { type.fields.forEach((field) => { collectField(field, typeName); }); } if (hasTypes(type)) { type.types.forEach((t) => { collectType(resolveType(t)); }); } if (hasInterfaces(type)) { type.interfaces.forEach((i) => { collectType(resolveType(i)); }); } }); } function resolveType(type) { if (type.kind === 'ListType') { return resolveType(type.type); } if (type.kind === 'NonNullType') { return resolveType(type.type); } return type.name.value; } function isScalar(name) { return scalars .concat(['String', 'Boolean', 'Int', 'ID', 'Float']) .includes(name); } function findTypes(typeName) { const types = schema.definitions.filter((def) => isNamedTypeDefinition(def) && def.name.value === typeName); if (!types.length) { throw new Error(`Missing type "${typeName}"`); } return types; } } function hasInterfaces(def) { return (hasPropValue(def, 'interfaces') && [ graphql_1.Kind.OBJECT_TYPE_DEFINITION, graphql_1.Kind.OBJECT_TYPE_EXTENSION, graphql_1.Kind.INTERFACE_TYPE_DEFINITION, graphql_1.Kind.INTERFACE_TYPE_EXTENSION, ].includes(def.kind)); } function hasTypes(def) { return ([graphql_1.Kind.UNION_TYPE_DEFINITION, graphql_1.Kind.UNION_TYPE_EXTENSION].includes(def.kind) && hasPropValue(def, 'types')); } function hasFields(def) { return ([ graphql_1.Kind.OBJECT_TYPE_DEFINITION, graphql_1.Kind.OBJECT_TYPE_EXTENSION, graphql_1.Kind.INTERFACE_TYPE_DEFINITION, graphql_1.Kind.INTERFACE_TYPE_EXTENSION, graphql_1.Kind.INPUT_OBJECT_TYPE_DEFINITION, graphql_1.Kind.INPUT_OBJECT_TYPE_EXTENSION, ].includes(def.kind) && hasPropValue(def, 'fields')); } function hasPropValue(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop) && obj[prop]; } function isRootType(typeName) { return (typeName === 'Query' || typeName === 'Mutation' || typeName === 'Subscription'); }