UNPKG

@graphql-codegen/visitor-plugin-common

Version:
711 lines (710 loc) • 35.3 kB
import { ApolloFederation, getBaseType } from '@graphql-codegen/plugin-helpers'; import { getRootTypeNames } from '@graphql-tools/utils'; import autoBind from 'auto-bind'; import { GraphQLObjectType, isEnumType, isInterfaceType, isNonNullType, isObjectType, isUnionType, } from 'graphql'; import { BaseVisitor } from './base-visitor.js'; import { parseEnumValues } from './enum-values.js'; import { buildMapperImport, parseMapper, transformMappers } from './mappers.js'; import { DEFAULT_SCALARS } from './scalars.js'; import { buildScalarsFromConfig, DeclarationBlock, getBaseTypeNode, getConfigValue, indent, OMIT_TYPE, REQUIRE_FIELDS_TYPE, stripMapperTypeInterpolation, wrapTypeWithModifiers, } from './utils.js'; import { OperationVariablesToObject } from './variables-to-object.js'; export class BaseResolversVisitor extends BaseVisitor { constructor(rawConfig, additionalConfig, _schema, defaultScalars = DEFAULT_SCALARS) { var _a; super(rawConfig, { immutableTypes: getConfigValue(rawConfig.immutableTypes, false), optionalResolveType: getConfigValue(rawConfig.optionalResolveType, false), enumPrefix: getConfigValue(rawConfig.enumPrefix, true), federation: getConfigValue(rawConfig.federation, false), resolverTypeWrapperSignature: getConfigValue(rawConfig.resolverTypeWrapperSignature, 'Promise<T> | T'), enumValues: parseEnumValues({ schema: _schema, mapOrStr: rawConfig.enumValues, }), addUnderscoreToArgsType: getConfigValue(rawConfig.addUnderscoreToArgsType, false), onlyResolveTypeForInterfaces: getConfigValue(rawConfig.onlyResolveTypeForInterfaces, false), contextType: parseMapper(rawConfig.contextType || 'any', 'ContextType'), fieldContextTypes: getConfigValue(rawConfig.fieldContextTypes, []), directiveContextTypes: getConfigValue(rawConfig.directiveContextTypes, []), resolverTypeSuffix: getConfigValue(rawConfig.resolverTypeSuffix, 'Resolvers'), allResolversTypeName: getConfigValue(rawConfig.allResolversTypeName, 'Resolvers'), rootValueType: parseMapper(rawConfig.rootValueType || '{}', 'RootValueType'), namespacedImportName: getConfigValue(rawConfig.namespacedImportName, ''), avoidOptionals: getConfigValue(rawConfig.avoidOptionals, false), defaultMapper: rawConfig.defaultMapper ? parseMapper(rawConfig.defaultMapper || 'any', 'DefaultMapperType') : null, mappers: transformMappers(rawConfig.mappers || {}, rawConfig.mapperTypeSuffix), scalars: buildScalarsFromConfig(_schema, rawConfig, defaultScalars), internalResolversPrefix: getConfigValue(rawConfig.internalResolversPrefix, '__'), ...additionalConfig, }); this._schema = _schema; this._declarationBlockConfig = {}; this._collectedResolvers = {}; this._collectedDirectiveResolvers = {}; this._usedMappers = {}; this._resolversTypes = {}; this._resolversParentTypes = {}; this._rootTypeNames = new Set(); this._globalDeclarations = new Set(); this._hasScalars = false; this._hasFederation = false; this._shouldMapType = {}; autoBind(this); this._federation = new ApolloFederation({ enabled: this.config.federation, schema: this.schema }); this._rootTypeNames = getRootTypeNames(_schema); this._variablesTransformer = new OperationVariablesToObject(this.scalars, this.convertName, this.config.namespacedImportName); this._resolversTypes = this.createResolversFields(type => this.applyResolverTypeWrapper(type), type => this.clearResolverTypeWrapper(type), name => this.getTypeToUse(name)); this._resolversParentTypes = this.createResolversFields(type => type, type => type, name => this.getParentTypeToUse(name), namedType => !isEnumType(namedType)); this._fieldContextTypeMap = this.createFieldContextTypeMap(); this._directiveContextTypesMap = this.createDirectivedContextType(); this._directiveResolverMappings = (_a = rawConfig.directiveResolverMappings) !== null && _a !== void 0 ? _a : {}; } getResolverTypeWrapperSignature() { return `export type ResolverTypeWrapper<T> = ${this.config.resolverTypeWrapperSignature};`; } shouldMapType(type, duringCheck = []) { if (type.name.startsWith('__') || this.config.scalars[type.name]) { return false; } if (this.config.mappers[type.name]) { return true; } if (isObjectType(type) || isInterfaceType(type)) { const fields = type.getFields(); return Object.keys(fields) .filter(fieldName => { const field = fields[fieldName]; const fieldType = getBaseType(field.type); return !duringCheck.includes(fieldType.name); }) .some(fieldName => { const field = fields[fieldName]; const fieldType = getBaseType(field.type); if (this._shouldMapType[fieldType.name] !== undefined) { return this._shouldMapType[fieldType.name]; } if (this.config.mappers[type.name]) { return true; } duringCheck.push(type.name); const innerResult = this.shouldMapType(fieldType, duringCheck); return innerResult; }); } return false; } convertName(node, options, applyNamespacedImport = false) { const sourceType = super.convertName(node, options); return `${applyNamespacedImport && this.config.namespacedImportName ? this.config.namespacedImportName + '.' : ''}${sourceType}`; } // Kamil: this one is heeeeavvyyyy createResolversFields(applyWrapper, clearWrapper, getTypeToUse, shouldInclude) { const allSchemaTypes = this._schema.getTypeMap(); const typeNames = this._federation.filterTypeNames(Object.keys(allSchemaTypes)); // avoid checking all types recursively if we have no `mappers` defined if (Object.keys(this.config.mappers).length > 0) { typeNames.forEach(typeName => { if (this._shouldMapType[typeName] === undefined) { const schemaType = allSchemaTypes[typeName]; this._shouldMapType[typeName] = this.shouldMapType(schemaType); } }); } return typeNames.reduce((prev, typeName) => { var _a; const schemaType = allSchemaTypes[typeName]; if (typeName.startsWith('__') || (shouldInclude && !shouldInclude(schemaType))) { return prev; } let shouldApplyOmit = false; const isRootType = this._rootTypeNames.has(typeName); const isMapped = this.config.mappers[typeName]; const isScalar = this.config.scalars[typeName]; const hasDefaultMapper = !!((_a = this.config.defaultMapper) === null || _a === void 0 ? void 0 : _a.type); if (isRootType) { prev[typeName] = applyWrapper(this.config.rootValueType.type); return prev; } if (isMapped && this.config.mappers[typeName].type) { this.markMapperAsUsed(typeName); prev[typeName] = applyWrapper(this.config.mappers[typeName].type); } else if (isInterfaceType(schemaType)) { const allTypesMap = this._schema.getTypeMap(); const implementingTypes = []; for (const graphqlType of Object.values(allTypesMap)) { if (graphqlType instanceof GraphQLObjectType) { const allInterfaces = graphqlType.getInterfaces(); if (allInterfaces.some(int => int.name === schemaType.name)) { implementingTypes.push(graphqlType.name); } } } const possibleTypes = implementingTypes.map(name => getTypeToUse(name)).join(' | ') || 'never'; prev[typeName] = possibleTypes; return prev; } else if (isEnumType(schemaType) && this.config.enumValues[typeName]) { prev[typeName] = this.config.enumValues[typeName].sourceIdentifier || this.convertName(this.config.enumValues[typeName].typeIdentifier); } else if (hasDefaultMapper && !hasPlaceholder(this.config.defaultMapper.type)) { prev[typeName] = applyWrapper(this.config.defaultMapper.type); } else if (isScalar) { prev[typeName] = applyWrapper(this._getScalar(typeName)); } else if (isUnionType(schemaType)) { prev[typeName] = schemaType .getTypes() .map(type => getTypeToUse(type.name)) .join(' | '); } else if (isEnumType(schemaType)) { prev[typeName] = this.convertName(typeName, { useTypesPrefix: this.config.enumPrefix }, true); } else { shouldApplyOmit = true; prev[typeName] = this.convertName(typeName, {}, true); } if (shouldApplyOmit && prev[typeName] !== 'any' && isObjectType(schemaType)) { const fields = schemaType.getFields(); const relevantFields = this._federation .filterFieldNames(Object.keys(fields)) .filter(fieldName => { const field = fields[fieldName]; const baseType = getBaseType(field.type); // Filter out fields of types that are not included if (shouldInclude && !shouldInclude(baseType)) { return false; } return true; }) .map(fieldName => { const field = fields[fieldName]; const baseType = getBaseType(field.type); const isUnion = isUnionType(baseType); if (!this.config.mappers[baseType.name] && !isUnion && !this._shouldMapType[baseType.name]) { return null; } const addOptionalSign = !this.config.avoidOptionals && !isNonNullType(field.type); return { addOptionalSign, fieldName, replaceWithType: wrapTypeWithModifiers(getTypeToUse(baseType.name), field.type, { wrapOptional: this.applyMaybe, wrapArray: this.wrapWithArray, }), }; }) .filter(a => a); if (relevantFields.length > 0) { // Puts ResolverTypeWrapper on top of an entire type prev[typeName] = applyWrapper(this.replaceFieldsInType(prev[typeName], relevantFields)); } else { // We still want to use ResolverTypeWrapper, even if we don't touch any fields prev[typeName] = applyWrapper(prev[typeName]); } } if (isMapped && hasPlaceholder(prev[typeName])) { prev[typeName] = replacePlaceholder(prev[typeName], typeName); } if (!isMapped && hasDefaultMapper && hasPlaceholder(this.config.defaultMapper.type)) { // Make sure the inner type has no ResolverTypeWrapper const name = clearWrapper(isScalar ? this._getScalar(typeName) : prev[typeName]); const replaced = replacePlaceholder(this.config.defaultMapper.type, name); // Don't wrap Union with ResolverTypeWrapper, each inner type already has it if (isUnionType(schemaType)) { prev[typeName] = replaced; } else { prev[typeName] = applyWrapper(replacePlaceholder(this.config.defaultMapper.type, name)); } } return prev; }, {}); } replaceFieldsInType(typeName, relevantFields) { this._globalDeclarations.add(OMIT_TYPE); return `Omit<${typeName}, ${relevantFields.map(f => `'${f.fieldName}'`).join(' | ')}> & { ${relevantFields .map(f => `${f.fieldName}${f.addOptionalSign ? '?' : ''}: ${f.replaceWithType}`) .join(', ')} }`; } applyMaybe(str) { const namespacedImportPrefix = this.config.namespacedImportName ? this.config.namespacedImportName + '.' : ''; return `${namespacedImportPrefix}Maybe<${str}>`; } applyResolverTypeWrapper(str) { return `ResolverTypeWrapper<${this.clearResolverTypeWrapper(str)}>`; } clearMaybe(str) { const namespacedImportPrefix = this.config.namespacedImportName ? this.config.namespacedImportName + '.' : ''; if (str.startsWith(`${namespacedImportPrefix}Maybe<`)) { const maybeRe = new RegExp(`${namespacedImportPrefix.replace('.', '\\.')}Maybe<(.*?)>$`); return str.replace(maybeRe, '$1'); } return str; } clearResolverTypeWrapper(str) { if (str.startsWith('ResolverTypeWrapper<')) { return str.replace(/ResolverTypeWrapper<(.*?)>$/, '$1'); } return str; } wrapWithArray(t) { if (this.config.immutableTypes) { return `ReadonlyArray<${t}>`; } return `Array<${t}>`; } createFieldContextTypeMap() { return this.config.fieldContextTypes.reduce((prev, fieldContextType) => { const items = fieldContextType.split('#'); if (items.length === 3) { const [path, source, contextTypeName] = items; return { ...prev, [path]: parseMapper(`${source}#${contextTypeName}`) }; } const [path, contextType] = items; return { ...prev, [path]: parseMapper(contextType) }; }, {}); } createDirectivedContextType() { return this.config.directiveContextTypes.reduce((prev, fieldContextType) => { const items = fieldContextType.split('#'); if (items.length === 3) { const [path, source, contextTypeName] = items; return { ...prev, [path]: parseMapper(`${source}#${contextTypeName}`) }; } const [path, contextType] = items; return { ...prev, [path]: parseMapper(contextType) }; }, {}); } buildResolversTypes() { const declarationKind = 'type'; return new DeclarationBlock(this._declarationBlockConfig) .export() .asKind(declarationKind) .withName(this.convertName('ResolversTypes')) .withComment('Mapping between all available schema types and the resolvers types') .withBlock(Object.keys(this._resolversTypes) .map(typeName => indent(`${typeName}: ${this._resolversTypes[typeName]}${this.getPunctuation(declarationKind)}`)) .join('\n')).string; } buildResolversParentTypes() { const declarationKind = 'type'; return new DeclarationBlock(this._declarationBlockConfig) .export() .asKind(declarationKind) .withName(this.convertName('ResolversParentTypes')) .withComment('Mapping between all available schema types and the resolvers parents') .withBlock(Object.keys(this._resolversParentTypes) .map(typeName => indent(`${typeName}: ${this._resolversParentTypes[typeName]}${this.getPunctuation(declarationKind)}`)) .join('\n')).string; } get schema() { return this._schema; } get defaultMapperType() { return this.config.defaultMapper.type; } get unusedMappers() { return Object.keys(this.config.mappers).filter(name => !this._usedMappers[name]); } get globalDeclarations() { return Array.from(this._globalDeclarations); } isMapperImported(groupedMappers, identifier, source) { const exists = groupedMappers[source] ? !!groupedMappers[source].find(m => m.identifier === identifier) : false; const existsFromEnums = !!Object.keys(this.config.enumValues) .map(key => this.config.enumValues[key]) .find(o => o.sourceFile === source && o.typeIdentifier === identifier); return exists || existsFromEnums; } get mappersImports() { var _a; const groupedMappers = {}; const addMapper = (source, identifier, asDefault) => { if (!this.isMapperImported(groupedMappers, identifier, source)) { groupedMappers[source] || (groupedMappers[source] = []); groupedMappers[source].push({ identifier, asDefault }); } }; Object.keys(this.config.mappers) .map(gqlTypeName => ({ gqlType: gqlTypeName, mapper: this.config.mappers[gqlTypeName] })) .filter(({ mapper }) => mapper.isExternal) .forEach(({ mapper }) => { const externalMapper = mapper; const identifier = stripMapperTypeInterpolation(externalMapper.import); addMapper(externalMapper.source, identifier, externalMapper.default); }); if (this.config.contextType.isExternal) { addMapper(this.config.contextType.source, this.config.contextType.import, this.config.contextType.default); } if (this.config.rootValueType.isExternal) { addMapper(this.config.rootValueType.source, this.config.rootValueType.import, this.config.rootValueType.default); } if ((_a = this.config.defaultMapper) === null || _a === void 0 ? void 0 : _a.isExternal) { const identifier = stripMapperTypeInterpolation(this.config.defaultMapper.import); addMapper(this.config.defaultMapper.source, identifier, this.config.defaultMapper.default); } Object.values(this._fieldContextTypeMap).forEach(parsedMapper => { if (parsedMapper.isExternal) { addMapper(parsedMapper.source, parsedMapper.import, parsedMapper.default); } }); Object.values(this._directiveContextTypesMap).forEach(parsedMapper => { if (parsedMapper.isExternal) { addMapper(parsedMapper.source, parsedMapper.import, parsedMapper.default); } }); return Object.keys(groupedMappers) .map(source => buildMapperImport(source, groupedMappers[source], this.config.useTypeImports)) .filter(Boolean); } setDeclarationBlockConfig(config) { this._declarationBlockConfig = config; } setVariablesTransformer(variablesTransfomer) { this._variablesTransformer = variablesTransfomer; } hasScalars() { return this._hasScalars; } hasFederation() { return this._hasFederation; } getRootResolver() { const name = this.convertName(this.config.allResolversTypeName); const declarationKind = 'type'; const contextType = `<ContextType = ${this.config.contextType.type}>`; return [ new DeclarationBlock(this._declarationBlockConfig) .export() .asKind(declarationKind) .withName(name, contextType) .withBlock(Object.keys(this._collectedResolvers) .map(schemaTypeName => { const resolverType = this._collectedResolvers[schemaTypeName]; return indent(this.formatRootResolver(schemaTypeName, resolverType, declarationKind)); }) .join('\n')).string, ].join('\n'); } formatRootResolver(schemaTypeName, resolverType, declarationKind) { return `${schemaTypeName}${this.config.avoidOptionals ? '' : '?'}: ${resolverType}${this.getPunctuation(declarationKind)}`; } getAllDirectiveResolvers() { if (Object.keys(this._collectedDirectiveResolvers).length) { const declarationKind = 'type'; const name = this.convertName('DirectiveResolvers'); const contextType = `<ContextType = ${this.config.contextType.type}>`; return [ new DeclarationBlock(this._declarationBlockConfig) .export() .asKind(declarationKind) .withName(name, contextType) .withBlock(Object.keys(this._collectedDirectiveResolvers) .map(schemaTypeName => { const resolverType = this._collectedDirectiveResolvers[schemaTypeName]; return indent(this.formatRootResolver(schemaTypeName, resolverType, declarationKind)); }) .join('\n')).string, ].join('\n'); } return ''; } Name(node) { return node.value; } ListType(node) { const asString = node.type; return this.wrapWithArray(asString); } _getScalar(name) { return `${this.config.namespacedImportName ? this.config.namespacedImportName + '.' : ''}Scalars['${name}']`; } NamedType(node) { const nameStr = node.name; if (this.config.scalars[nameStr]) { return this._getScalar(nameStr); } return this.convertName(node, null, true); } NonNullType(node) { const asString = node.type; return asString; } markMapperAsUsed(name) { this._usedMappers[name] = true; } getTypeToUse(name) { const resolversType = this.convertName('ResolversTypes'); return `${resolversType}['${name}']`; } getParentTypeToUse(name) { const resolversType = this.convertName('ResolversParentTypes'); return `${resolversType}['${name}']`; } getParentTypeForSignature(_node) { return 'ParentType'; } transformParentGenericType(parentType) { return `ParentType extends ${parentType} = ${parentType}`; } FieldDefinition(node, key, parent) { const hasArguments = node.arguments && node.arguments.length > 0; const declarationKind = 'type'; return (parentName) => { var _a, _b, _c, _d, _e; const original = parent[key]; const baseType = getBaseTypeNode(original.type); const realType = baseType.name.value; const parentType = this.schema.getType(parentName); if (this._federation.skipField({ fieldNode: original, parentType })) { return null; } const contextType = this.getContextType(parentName, node); const typeToUse = this.getTypeToUse(realType); const mappedType = this._variablesTransformer.wrapAstTypeWithModifiers(typeToUse, original.type); const subscriptionType = this._schema.getSubscriptionType(); const isSubscriptionType = subscriptionType && subscriptionType.name === parentName; let argsType = hasArguments ? this.convertName(parentName + (this.config.addUnderscoreToArgsType ? '_' : '') + this.convertName(node.name, { useTypesPrefix: false, useTypesSuffix: false, }) + 'Args', { useTypesPrefix: true, }, true) : null; if (argsType !== null) { const argsToForceRequire = original.arguments.filter(arg => !!arg.defaultValue || arg.type.kind === 'NonNullType'); if (argsToForceRequire.length > 0) { argsType = this.applyRequireFields(argsType, argsToForceRequire); } else if (original.arguments.length > 0) { argsType = this.applyOptionalFields(argsType, original.arguments); } } const parentTypeSignature = this._federation.transformParentType({ fieldNode: original, parentType, parentTypeSignature: this.getParentTypeForSignature(node), }); const mappedTypeKey = isSubscriptionType ? `${mappedType}, "${node.name}"` : mappedType; const directiveMappings = (_b = (_a = node.directives) === null || _a === void 0 ? void 0 : _a.map(directive => this._directiveResolverMappings[directive.name]).filter(Boolean).reverse()) !== null && _b !== void 0 ? _b : []; const resolverType = isSubscriptionType ? 'SubscriptionResolver' : (_c = directiveMappings[0]) !== null && _c !== void 0 ? _c : 'Resolver'; const avoidOptionals = (_e = (_d = this.config.avoidOptionals) === null || _d === void 0 ? void 0 : _d.resolvers) !== null && _e !== void 0 ? _e : this.config.avoidOptionals === true; const signature = { name: node.name, modifier: avoidOptionals ? '' : '?', type: resolverType, genericTypes: [mappedTypeKey, parentTypeSignature, contextType, argsType].filter(f => f), }; if (this._federation.isResolveReferenceField(node)) { this._hasFederation = true; signature.type = 'ReferenceResolver'; if (signature.genericTypes.length >= 3) { signature.genericTypes = signature.genericTypes.slice(0, 3); } } return indent(`${signature.name}${signature.modifier}: ${signature.type}<${signature.genericTypes.join(', ')}>${this.getPunctuation(declarationKind)}`); }; } getFieldContextType(parentName, node) { if (this._fieldContextTypeMap[`${parentName}.${node.name}`]) { return this._fieldContextTypeMap[`${parentName}.${node.name}`].type; } return 'ContextType'; } getContextType(parentName, node) { let contextType = this.getFieldContextType(parentName, node); for (const directive of node.directives) { const name = directive.name; const directiveMap = this._directiveContextTypesMap[name]; if (directiveMap) { contextType = `${directiveMap.type}<${contextType}>`; } } return contextType; } applyRequireFields(argsType, fields) { this._globalDeclarations.add(REQUIRE_FIELDS_TYPE); return `RequireFields<${argsType}, ${fields.map(f => `'${f.name.value}'`).join(' | ')}>`; } applyOptionalFields(argsType, _fields) { return `Partial<${argsType}>`; } ObjectTypeDefinition(node) { var _a, _b, _c; const declarationKind = 'type'; const name = this.convertName(node, { suffix: this.config.resolverTypeSuffix, }); const typeName = node.name; const parentType = this.getParentTypeToUse(typeName); const isRootType = [ (_a = this.schema.getQueryType()) === null || _a === void 0 ? void 0 : _a.name, (_b = this.schema.getMutationType()) === null || _b === void 0 ? void 0 : _b.name, (_c = this.schema.getSubscriptionType()) === null || _c === void 0 ? void 0 : _c.name, ].includes(typeName); const fieldsContent = node.fields.map((f) => f(node.name)); if (!isRootType) { fieldsContent.push(indent(`${this.config.internalResolversPrefix}isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>${this.getPunctuation(declarationKind)}`)); } const block = new DeclarationBlock(this._declarationBlockConfig) .export() .asKind(declarationKind) .withName(name, `<ContextType = ${this.config.contextType.type}, ${this.transformParentGenericType(parentType)}>`) .withBlock(fieldsContent.join('\n')); this._collectedResolvers[node.name] = name + '<ContextType>'; return block.string; } UnionTypeDefinition(node, key, parent) { const declarationKind = 'type'; const name = this.convertName(node, { suffix: this.config.resolverTypeSuffix, }); const originalNode = parent[key]; const possibleTypes = originalNode.types .map(node => node.name.value) .map(f => `'${f}'`) .join(' | '); this._collectedResolvers[node.name] = name + '<ContextType>'; const parentType = this.getParentTypeToUse(node.name); return new DeclarationBlock(this._declarationBlockConfig) .export() .asKind(declarationKind) .withName(name, `<ContextType = ${this.config.contextType.type}, ${this.transformParentGenericType(parentType)}>`) .withBlock(indent(`${this.config.internalResolversPrefix}resolveType${this.config.optionalResolveType ? '?' : ''}: TypeResolveFn<${possibleTypes}, ParentType, ContextType>${this.getPunctuation(declarationKind)}`)).string; } ScalarTypeDefinition(node) { const nameAsString = node.name; const baseName = this.getTypeToUse(nameAsString); if (this._federation.skipScalar(nameAsString)) { return null; } this._hasScalars = true; this._collectedResolvers[node.name] = 'GraphQLScalarType'; return new DeclarationBlock({ ...this._declarationBlockConfig, blockTransformer(block) { return block; }, }) .export() .asKind('interface') .withName(this.convertName(node, { suffix: 'ScalarConfig', }), ` extends GraphQLScalarTypeConfig<${baseName}, any>`) .withBlock(indent(`name: '${node.name}'${this.getPunctuation('interface')}`)).string; } DirectiveDefinition(node, key, parent) { if (this._federation.skipDirective(node.name)) { return null; } const directiveName = this.convertName(node, { suffix: 'DirectiveResolver', }); const sourceNode = parent[key]; const hasArguments = sourceNode.arguments && sourceNode.arguments.length > 0; this._collectedDirectiveResolvers[node.name] = directiveName + '<any, any, ContextType>'; const directiveArgsTypeName = this.convertName(node, { suffix: 'DirectiveArgs', }); return [ new DeclarationBlock({ ...this._declarationBlockConfig, blockTransformer(block) { return block; }, }) .export() .asKind('type') .withName(directiveArgsTypeName) .withContent(hasArguments ? `{\n${this._variablesTransformer.transform(sourceNode.arguments)}\n}` : '{ }').string, new DeclarationBlock({ ...this._declarationBlockConfig, blockTransformer(block) { return block; }, }) .export() .asKind('type') .withName(directiveName, `<Result, Parent, ContextType = ${this.config.contextType.type}, Args = ${directiveArgsTypeName}>`) .withContent(`DirectiveResolverFn<Result, Parent, ContextType, Args>`).string, ].join('\n'); } buildEnumResolverContentBlock(_node, _mappedEnumType) { throw new Error(`buildEnumResolverContentBlock is not implemented!`); } buildEnumResolversExplicitMappedValues(_node, _valuesMapping) { throw new Error(`buildEnumResolversExplicitMappedValues is not implemented!`); } EnumTypeDefinition(node) { var _a; const rawTypeName = node.name; // If we have enumValues set, and it's point to an external enum - we need to allow internal values resolvers // In case we have enumValues set but as explicit values, no need to to do mapping since it's already // have type validation (the original enum has been modified by base types plugin). // If we have mapper for that type - we can skip if (!this.config.mappers[rawTypeName] && !this.config.enumValues[rawTypeName]) { return null; } const name = this.convertName(node, { suffix: this.config.resolverTypeSuffix }); this._collectedResolvers[rawTypeName] = name; const hasExplicitValues = (_a = this.config.enumValues[rawTypeName]) === null || _a === void 0 ? void 0 : _a.mappedValues; return new DeclarationBlock(this._declarationBlockConfig) .export() .asKind('type') .withName(name) .withContent(hasExplicitValues ? this.buildEnumResolversExplicitMappedValues(node, this.config.enumValues[rawTypeName].mappedValues) : this.buildEnumResolverContentBlock(node, this.getTypeToUse(rawTypeName))).string; } InterfaceTypeDefinition(node) { const name = this.convertName(node, { suffix: this.config.resolverTypeSuffix, }); const declarationKind = 'type'; const allTypesMap = this._schema.getTypeMap(); const implementingTypes = []; this._collectedResolvers[node.name] = name + '<ContextType>'; for (const graphqlType of Object.values(allTypesMap)) { if (graphqlType instanceof GraphQLObjectType) { const allInterfaces = graphqlType.getInterfaces(); if (allInterfaces.find(int => int.name === node.name)) { implementingTypes.push(graphqlType.name); } } } const parentType = this.getParentTypeToUse(node.name); const possibleTypes = implementingTypes.map(name => `'${name}'`).join(' | ') || 'null'; const fields = this.config.onlyResolveTypeForInterfaces ? [] : node.fields || []; return new DeclarationBlock(this._declarationBlockConfig) .export() .asKind(declarationKind) .withName(name, `<ContextType = ${this.config.contextType.type}, ${this.transformParentGenericType(parentType)}>`) .withBlock([ indent(`${this.config.internalResolversPrefix}resolveType${this.config.optionalResolveType ? '?' : ''}: TypeResolveFn<${possibleTypes}, ParentType, ContextType>${this.getPunctuation(declarationKind)}`), ...fields.map((f) => f(node.name)), ].join('\n')).string; } SchemaDefinition() { return null; } } function replacePlaceholder(pattern, typename) { return pattern.replace(/\{T\}/g, typename); } function hasPlaceholder(pattern) { return pattern.includes('{T}'); }