UNPKG

@eddeee888/gcg-typescript-resolver-files

Version:

This [GraphQL Code Generator](https://www.the-guild.dev/graphql/codegen) plugin creates resolvers given GraphQL schema.

168 lines 8.42 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getGraphQLObjectTypeResolversToGenerate = void 0; const ts_morph_1 = require("ts-morph"); const getNodePropertyMap_1 = require("./getNodePropertyMap"); const getGraphQLObjectTypeResolversToGenerate = ({ tsMorphProject, typesSourceFile, typeMappersMap, userDefinedSchemaObjectTypeMap, generatedTypesFileMeta, }) => { const typeMappersEntries = Object.entries(typeMappersMap); if (typeMappersEntries.length === 0) { return {}; } /** * `generatedTypesFileMeta.generatedResolverTypes.userDefined` is `schemaType` -> `generatedResolverTypes` * We will be parsing `types.generated.ts` file for the `generatedResolverTypes` * So, we need to invert `generatedTypesFileMeta.generatedResolverTypes.userDefined` to get `generatedResolverTypes` -> `schemaType` * e.g. * * generatedTypesFileMeta.generatedResolverTypes.userDefined = { * User: { * name: 'UserResolvers' * } * } * * after transformation: * * generatedSchemaTypeNameMap = { * UserResolvers: 'User' * } */ const generatedSchemaTypeNameMap = Object.entries(generatedTypesFileMeta.generatedResolverTypes.userDefined).reduce((res, [schemaType, generatedSchemaTypeName]) => { res[generatedSchemaTypeName.name] = schemaType; return res; }, {}); // 1. Get property map of all schema types const schemaResolversTypePropertyMap = {}; const populateSchemaTypeResolversPropertyMap = (node) => { const identifier = node.getNameNode(); const identifierName = identifier.getText(); const schemaType = generatedSchemaTypeNameMap[identifierName]; if (schemaType && userDefinedSchemaObjectTypeMap[schemaType]) { schemaResolversTypePropertyMap[schemaType] = (0, getNodePropertyMap_1.getNodePropertyMap)({ tsMorphProject, node, }); } }; typesSourceFile .getDescendantsOfKind(ts_morph_1.SyntaxKind.TypeAliasDeclaration) .forEach(populateSchemaTypeResolversPropertyMap); typesSourceFile .getDescendantsOfKind(ts_morph_1.SyntaxKind.InterfaceDeclaration) .forEach(populateSchemaTypeResolversPropertyMap); // 3. Find resolvers to generate and add reason const result = {}; typeMappersEntries.forEach(([_, { schemaType, mapper }]) => { const matchedSchemaTypePropertyMap = schemaResolversTypePropertyMap[schemaType]; if (!matchedSchemaTypePropertyMap) { return; } const originalDeclarationNode = mustGetMapperOriginalDeclarationNode({ tsMorphProject, mapper, }); const typeMapperPropertyMap = (0, getNodePropertyMap_1.getNodePropertyMap)({ tsMorphProject, node: originalDeclarationNode, }); Object.values(matchedSchemaTypePropertyMap).forEach((schemaTypeProperty) => { const typeMapperProperty = typeMapperPropertyMap[schemaTypeProperty.name]; const typeMapperPropertyIdentifier = `${mapper.name}.${schemaTypeProperty.name}`; const schemaTypePropertyIdentifier = `${schemaType}.${schemaTypeProperty.name}`; // Generated resolvers types may have one or more of these meta resolvers // A mapper would most likely never have these resolvers, so we skip them // Otherwise, these resolvers will always be generated const metaResolvers = { __isTypeOf: true, __resolveReference: true, }; if (metaResolvers[schemaTypeProperty.name]) { return; } result[schemaType] = result[schemaType] || {}; // If mapper does not have a field in schema type, add missing resolver if (!typeMapperProperty) { result[schemaType][schemaTypeProperty.name] = { resolverName: schemaTypeProperty.name, resolverDeclaration: `async (_parent, _arg, _ctx) => { /* ${schemaTypePropertyIdentifier} resolver is required because ${schemaTypePropertyIdentifier} exists but ${typeMapperPropertyIdentifier} does not */ }`, }; return; } /** * FIXME: TypeScript's `isTypeAssignableTo` should be used to check if the mapper type vs resolver return type is compatible. * The current challenge is to: * - Switch from using the schema type to resolver return type e.g. `User` -> `UserResolver` * - Take the ReturnType of the resolver function type e.g. `Resolver<ResolversTypes['UserRole'], ParentType, ContextType>` * * For now, the workaround now is to generate all resolvers with matching names, * then use TS diagnostics to see if there's error when trying to merge the two keys. * * Note: this happens only when mappers are used */ result[schemaType][schemaTypeProperty.name] = { resolverName: schemaTypeProperty.name, resolverDeclaration: `({ ${schemaTypeProperty.name} }, _arg, _ctx) => { /* ${schemaTypePropertyIdentifier} resolver is required because ${schemaTypePropertyIdentifier} and ${typeMapperPropertyIdentifier} are not compatible */ return ${schemaTypeProperty.name} }`, }; return; }); }); return result; }; exports.getGraphQLObjectTypeResolversToGenerate = getGraphQLObjectTypeResolversToGenerate; const mustGetMapperOriginalDeclarationNode = ({ tsMorphProject, mapper, }) => { const typeMapperFile = tsMorphProject.getSourceFile(mapper.filename); if (!typeMapperFile) { throw new Error(`Unable to find ${typeMapperFile} file after parsing. This shouldn't happen.`); } /** * Finding `firstDescendantThatIsMapper` here is a bit of the duplicated traversing logic in `collectTypeMappersFromSourceFile`. * However, in `collectTypeMappersFromSourceFile`, we find the mappers details. * And here, we actually do look for the mapper nodes and run analysis on it. * * Previously, we were parsing the node property map in `collectTypeMappersFromSourceFile` * but for some reason `isAssignableTo` has issue comparing types, so we have to move the static analysis here for now. */ const firstDescendantThatIsMapper = (() => { for (const descendant of typeMapperFile.getDescendants()) { const typedNode = descendant.isKind(mapper.kind); if (typedNode) { let identifierNode = descendant.getNameNode(); if (descendant.isKind(ts_morph_1.SyntaxKind.ExportSpecifier)) { const aliasNode = descendant.getAliasNode(); if (aliasNode) { identifierNode = aliasNode; } } if ((identifierNode === null || identifierNode === void 0 ? void 0 : identifierNode.getText()) === mapper.name) { return { declarationNode: descendant, identifierNode, }; } } } return; })(); if (!firstDescendantThatIsMapper) { throw new Error(`Unable to find ${mapper.name} node after parsing. This shouldn't happen.`); } return getOriginalDeclarationNode(firstDescendantThatIsMapper); }; const getOriginalDeclarationNode = ({ declarationNode, identifierNode, }) => { if (declarationNode.isKind(ts_morph_1.SyntaxKind.ExportSpecifier) || declarationNode.isKind(ts_morph_1.SyntaxKind.ClassDeclaration)) { return identifierNode.getDefinitionNodes()[0]; } // InterfaceDeclaration if (declarationNode.isKind(ts_morph_1.SyntaxKind.InterfaceDeclaration)) { return declarationNode; } // TypeAliasDeclaration const typeNode = declarationNode.getTypeNodeOrThrow(); return ts_morph_1.Node.isTypeReference(typeNode) ? identifierNode.getDefinitionNodes()[0] // If type alias is a reference, go to definition using `getDefinitionNodes` : declarationNode; }; //# sourceMappingURL=getGraphQLObjectTypeResolversToGenerate.js.map