UNPKG

@graphql-codegen/graphql-modules-preset

Version:

GraphQL Code Generator preset for modularized schema

345 lines (344 loc) • 13.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.buildModule = buildModule; const change_case_all_1 = require("change-case-all"); const graphql_1 = require("graphql"); const utils_js_1 = require("./utils.js"); const registryKeys = ['objects', 'inputs', 'interfaces', 'scalars', 'unions', 'enums']; const resolverKeys = ['scalars', 'objects', 'enums']; function buildModule(name, doc, { importNamespace, importPath, encapsulate, requireRootResolvers, shouldDeclare, rootTypes, schema, baseVisitor, useGraphQLModules, useTypeImports = false, }) { const picks = (0, utils_js_1.createObject)(registryKeys, () => ({})); const defined = (0, utils_js_1.createObject)(registryKeys, () => []); const extended = (0, utils_js_1.createObject)(registryKeys, () => []); // List of types used in objects, fields, arguments etc const usedTypes = (0, utils_js_1.collectUsedTypes)(doc); (0, graphql_1.visit)(doc, { ObjectTypeDefinition(node) { collectTypeDefinition(node); }, ObjectTypeExtension(node) { collectTypeExtension(node); }, InputObjectTypeDefinition(node) { collectTypeDefinition(node); }, InputObjectTypeExtension(node) { collectTypeExtension(node); }, InterfaceTypeDefinition(node) { collectTypeDefinition(node); }, InterfaceTypeExtension(node) { collectTypeExtension(node); }, ScalarTypeDefinition(node) { collectTypeDefinition(node); }, UnionTypeDefinition(node) { collectTypeDefinition(node); }, UnionTypeExtension(node) { collectTypeExtension(node); }, EnumTypeDefinition(node) { collectTypeDefinition(node); }, EnumTypeExtension(node) { collectTypeExtension(node); }, }); // Defined and Extended types const visited = (0, utils_js_1.createObject)(registryKeys, key => (0, utils_js_1.concatByKey)(defined, extended, key)); // Types that are not defined or extended in a module, they come from other modules const external = (0, utils_js_1.createObject)(registryKeys, key => (0, utils_js_1.uniqueByKey)(extended, defined, key)); // // // // Prints // // // // An actual output const imports = [`import${useTypeImports ? ' type' : ''} * as ${importNamespace} from "${importPath}";`]; if (useGraphQLModules) { imports.push(`import${useTypeImports ? ' type' : ''} * as gm from "graphql-modules";`); } let content = [ printDefinedFields(), printDefinedEnumValues(), printDefinedInputFields(), printSchemaTypes(usedTypes), printScalars(visited), printResolveSignaturesPerType(visited), printResolversType(visited), useGraphQLModules ? printResolveMiddlewareMap() : undefined, ] .filter(Boolean) .join('\n\n'); if (encapsulate === 'namespace') { content = `${shouldDeclare ? 'declare' : 'export'} namespace ${baseVisitor.convertName(name, { suffix: 'Module', useTypesPrefix: false, useTypesSuffix: false, })} {\n` + (shouldDeclare ? `${(0, utils_js_1.indent)(2)(imports.join('\n'))}\n` : '') + (0, utils_js_1.indent)(2)(content) + '\n}'; } return [...(shouldDeclare ? [] : imports), content].filter(Boolean).join('\n'); /** * A dictionary of fields to pick from an object */ function printDefinedFields() { return (0, utils_js_1.buildBlock)({ name: `interface DefinedFields`, lines: [...visited.objects, ...visited.interfaces].map(typeName => `${typeName}: ${printPicks(typeName, { ...picks.objects, ...picks.interfaces, })};`), }); } /** * A dictionary of values to pick from an enum */ function printDefinedEnumValues() { return (0, utils_js_1.buildBlock)({ name: `interface DefinedEnumValues`, lines: visited.enums.map(typeName => `${typeName}: ${printPicks(typeName, picks.enums)};`), }); } function encapsulateTypeName(typeName) { if (encapsulate === 'prefix') { return `${(0, change_case_all_1.pascalCase)(name)}_${typeName}`; } return typeName; } /** * A dictionary of fields to pick from an input */ function printDefinedInputFields() { return (0, utils_js_1.buildBlock)({ name: `interface DefinedInputFields`, lines: visited.inputs.map(typeName => `${typeName}: ${printPicks(typeName, picks.inputs)};`), }); } /** * Prints signatures of schema types with picks */ function printSchemaTypes(types) { return types .filter(type => !visited.scalars.includes(type)) .map(printExportType) .join('\n'); } function printResolveSignaturesPerType(registry) { return [ [...registry.objects, ...registry.interfaces] .map(name => printResolverType(name, 'DefinedFields', // In case of enabled `requireRootResolvers` flag, the preset has to produce a non-optional properties. requireRootResolvers && rootTypes.includes(name), !rootTypes.includes(name) && defined.objects.includes(name) ? ` | '__isTypeOf'` : '')) .join('\n'), ].join('\n'); } function printScalars(registry) { if (!registry.scalars.length) { return ''; } return [ `export type ${encapsulateTypeName('Scalars')} = Pick<${importNamespace}.Scalars, ${registry.scalars .map(utils_js_1.withQuotes) .join(' | ')}>;`, ...registry.scalars.map(scalar => { const convertedName = baseVisitor.convertName(scalar, { suffix: 'ScalarConfig', }); return `export type ${encapsulateTypeName(convertedName)} = ${importNamespace}.${convertedName};`; }), ].join('\n'); } /** * Aggregation of type resolver signatures */ function printResolversType(registry) { const lines = []; for (const kind in registry) { const k = kind; if (Object.prototype.hasOwnProperty.call(registry, k) && resolverKeys.includes(k)) { const types = registry[k]; for (const typeName of types) { if (k === 'enums') { continue; } if (k === 'scalars') { lines.push(`${typeName}?: ${encapsulateTypeName(importNamespace)}.Resolvers['${typeName}'];`); } else { // In case of enabled `requireRootResolvers` flag, the preset has to produce a non-optional property. const fieldModifier = requireRootResolvers && rootTypes.includes(typeName) ? '' : '?'; lines.push(`${typeName}${fieldModifier}: ${encapsulateTypeName(typeName)}Resolvers;`); } } } } return (0, utils_js_1.buildBlock)({ name: `export interface ${encapsulateTypeName('Resolvers')}`, lines, }); } /** * Signature for a map of resolve middlewares */ function printResolveMiddlewareMap() { const wildcardField = printResolveMiddlewareRecord((0, utils_js_1.withQuotes)('*')); const blocks = [(0, utils_js_1.buildBlock)({ name: `${(0, utils_js_1.withQuotes)('*')}?:`, lines: [wildcardField] })]; // Type.Field for (const typeName in picks.objects) { if (Object.prototype.hasOwnProperty.call(picks.objects, typeName)) { const fields = picks.objects[typeName]; const lines = [wildcardField].concat(fields.map(field => printResolveMiddlewareRecord(field))); blocks.push((0, utils_js_1.buildBlock)({ name: `${typeName}?:`, lines, })); } } return (0, utils_js_1.buildBlock)({ name: `export interface ${encapsulateTypeName('MiddlewareMap')}`, lines: blocks, }); } function printResolveMiddlewareRecord(path) { return `${path}?: gm.Middleware[];`; } function printResolverType(typeName, picksTypeName, requireFieldsResolvers = false, extraKeys = '') { const typeSignature = `Pick<${importNamespace}.${baseVisitor.convertName(typeName, { suffix: 'Resolvers', })}, ${picksTypeName}['${typeName}']${extraKeys}>`; return `export type ${encapsulateTypeName(`${typeName}Resolvers`)} = ${requireFieldsResolvers ? `Required<${typeSignature}>` : typeSignature};`; } function printPicks(typeName, records) { return records[typeName].filter(utils_js_1.unique).map(utils_js_1.withQuotes).join(' | '); } function printTypeBody(typeName) { const coreType = `${importNamespace}.${baseVisitor.convertName(typeName, { useTypesSuffix: true, useTypesPrefix: true, })}`; if (external.enums.includes(typeName) || external.objects.includes(typeName)) { if (schema && (0, graphql_1.isScalarType)(schema.getType(typeName))) { return `${importNamespace}.Scalars['${typeName}']`; } return coreType; } if (defined.enums.includes(typeName) && picks.enums[typeName]) { return `DefinedEnumValues['${typeName}']`; } if (defined.objects.includes(typeName) && picks.objects[typeName]) { return `Pick<${coreType}, DefinedFields['${typeName}']>`; } if (defined.interfaces.includes(typeName) && picks.interfaces[typeName]) { return `Pick<${coreType}, DefinedFields['${typeName}']>`; } if (defined.inputs.includes(typeName) && picks.inputs[typeName]) { return `Pick<${coreType}, DefinedInputFields['${typeName}']>`; } return coreType; } function printExportType(typeName) { return `export type ${encapsulateTypeName(typeName)} = ${printTypeBody(typeName)};`; } // // // // Utils // // // function collectFields(node, picksObj) { const name = node.name.value; if (node.fields) { picksObj[name] ||= []; for (const field of node.fields) { picksObj[name].push(field.name.value); } } } function collectValuesFromEnum(node) { const name = node.name.value; if (node.values) { picks.enums[name] ||= []; for (const field of node.values) { picks.enums[name].push(field.name.value); } } } function collectTypeDefinition(node) { const name = node.name.value; switch (node.kind) { case graphql_1.Kind.OBJECT_TYPE_DEFINITION: { defined.objects.push(name); collectFields(node, picks.objects); break; } case graphql_1.Kind.ENUM_TYPE_DEFINITION: { defined.enums.push(name); collectValuesFromEnum(node); break; } case graphql_1.Kind.INPUT_OBJECT_TYPE_DEFINITION: { defined.inputs.push(name); collectFields(node, picks.inputs); break; } case graphql_1.Kind.SCALAR_TYPE_DEFINITION: { defined.scalars.push(name); break; } case graphql_1.Kind.INTERFACE_TYPE_DEFINITION: { defined.interfaces.push(name); collectFields(node, picks.interfaces); break; } case graphql_1.Kind.UNION_TYPE_DEFINITION: { defined.unions.push(name); break; } } } function collectTypeExtension(node) { const name = node.name.value; switch (node.kind) { case graphql_1.Kind.OBJECT_TYPE_EXTENSION: { collectFields(node, picks.objects); // Do not include root types as extensions // so we can use them in DefinedFields if (rootTypes.includes(name)) { (0, utils_js_1.pushUnique)(defined.objects, name); return; } (0, utils_js_1.pushUnique)(extended.objects, name); break; } case graphql_1.Kind.ENUM_TYPE_EXTENSION: { collectValuesFromEnum(node); (0, utils_js_1.pushUnique)(extended.enums, name); break; } case graphql_1.Kind.INPUT_OBJECT_TYPE_EXTENSION: { collectFields(node, picks.inputs); (0, utils_js_1.pushUnique)(extended.inputs, name); break; } case graphql_1.Kind.INTERFACE_TYPE_EXTENSION: { collectFields(node, picks.interfaces); (0, utils_js_1.pushUnique)(extended.interfaces, name); break; } case graphql_1.Kind.UNION_TYPE_EXTENSION: { (0, utils_js_1.pushUnique)(extended.unions, name); break; } } } }