UNPKG

@cortexql/ts2graphql

Version:

A TypeScrpt transpiler to GraphQL for your project

1,034 lines 62 kB
"use strict"; /* * This class reads your project via typedoc and transpile your project to * graphql */ Object.defineProperty(exports, "__esModule", { value: true }); const TypeDoc_1 = require("./TypeDoc"); const graphql_1 = require("graphql"); const values_1 = require("./values"); const appRootDir = require("app-root-dir"); const path_1 = require("path"); const capitalize_1 = require("../utils/capitalize"); const lowerFirst_1 = require("../utils/lowerFirst"); function hasTag(ref, tagName) { if (ref.comment !== undefined && ref.comment.tags !== undefined) { for (const { tag, text } of ref.comment.tags) { if (tag === tagName) { return true; } } } return false; } function getTag(ref, tagName) { if (ref.comment !== undefined && ref.comment.tags !== undefined) { for (const { tag, text } of ref.comment.tags) { if (tag === tagName) { return text; } } } return undefined; } function getTags(ref, tagName) { const values = []; if (ref.comment !== undefined && ref.comment.tags !== undefined) { for (const { tag, text } of ref.comment.tags) { if (tag === tagName) { values.push(text); } } } return values; } function injectCustomAttributes(type, resolveFields) { const getFieldDefault = type.getFields; const getFields = () => { const fields = getFieldDefault.call(type); const resolvedFields = resolveFields(); for (const key of Object.keys(fields)) { const field = fields[key]; if (!resolvedFields.hasOwnProperty(key)) { continue; } const resolvedField = resolvedFields[key]; if (resolvedField.hasOwnProperty('validate')) { field.validate = resolvedField.validate; } if (resolvedField.hasOwnProperty('sanitize')) { field.sanitize = resolvedField.sanitize; } if (field.hasOwnProperty('args') && resolvedField.hasOwnProperty('args')) { for (const index in field.args) { const argName = field.args[index].name; if (resolvedField.args[argName].hasOwnProperty('validate')) { field.args[index].validate = resolvedField.args[argName].validate; } if (resolvedField.args[argName].hasOwnProperty('sanitize')) { field.args[index].sanitize = resolvedField.args[argName].sanitize; } } } } return fields; }; Object.defineProperty(type, 'getFields', { value: getFields, configurable: true, }); } const indexNameRegExp = /(^"?|\/)index"?$/; /** * A simple helper that wraps any GraphQL Object and tell us it's optional */ class GraphQLOptionalHelper { constructor(type) { this.type = type; } getType() { return this.type; } } class TypeDoc2GraphQL { constructor(options) { this.resolvedTypes = {}; this.options = options; if (this.options.types !== undefined) { for (const type of this.options.types) { this.resolvedTypes[type.name] = type; } } const defaultPaths = { types: 'types', query: 'query', mutation: 'mutation', subscription: 'subscription', }; this.paths = Object.assign(defaultPaths, this.options.paths); if (options.parserHandler !== undefined) { this.parserHandler = options.parserHandler; } } initialize(doc) { this.typedoc = doc instanceof TypeDoc_1.TypeDoc ? doc : new TypeDoc_1.TypeDoc(doc); } getTypeDoc() { if (this.typedoc === undefined) { throw new Error('Transpiler wasn\'t initialized'); } return this.typedoc; } relativeError(message, ref, externalModule) { if (ref.sources === undefined || ref.sources.length < 1) { const parent = this.getTypeDoc().getParent(ref.id); if (parent !== undefined) { return this.relativeError(message, parent, externalModule); } return new Error(message); } if (externalModule === undefined || externalModule.originalName === undefined) { const foundExternalModule = this.getTypeDoc().getParentExternalModule(ref.id); if (foundExternalModule !== undefined) { return this.relativeError(message, ref, foundExternalModule); } return new Error(message); } return new Error(`${message} in ${buildSourcePath(Object.assign({}, ref.sources[0], { fileName: path_1.relative(appRootDir.get(), externalModule.originalName) }))}`); } getFileExport(file, subscribe = false) { const name = file.name.substr(1, file.name.length - 2).split('/').pop(); const children = file.children !== undefined ? file.children : []; for (const child of children) { if (name === undefined) { continue; } if (child.name === name) { return child; } const upperCased = capitalize_1.capitalize(name); if (subscribe) { if (child.name === `subscribe${upperCased}`) { return Object.assign({}, child, { name }); } if (child.name === 'subscribe') { return Object.assign({}, child, { name }); } } else { if (child.name === `resolve${upperCased}`) { return Object.assign({}, child, { name }); } if (child.name === 'resolve') { return Object.assign({}, child, { name }); } } } throw this.relativeError(`You must export ${name}`, file); } resolveIntrinsicType(type, ref) { switch (type.name) { case 'string': return graphql_1.GraphQLString; case 'number': return graphql_1.GraphQLFloat; case 'boolean': return graphql_1.GraphQLBoolean; case 'any': if (this.resolvedTypes.Any !== undefined) { return this.resolvedTypes.Any; } if (this.resolvedTypes.JSON !== undefined) { return this.resolvedTypes.JSON; } throw this.relativeError(`Unless you specify an external type named Any or JSON, yout wont be able to resolve intrinsic type "any"`, ref); } console.log(ref); throw this.relativeError(`Could not resolve intrinsic type "${type.name}"`, ref); } resolveExternalUnionType(typeRef, ref) { const types = typeRef.types.filter((child) => !isNullable(child)); if (types.length === 2) { if (types[0].name === 'true' && types[1].name === 'false') { const type = graphql_1.GraphQLBoolean; if (typeRef.types !== undefined && typeRef.types.length > 1) { return new GraphQLOptionalHelper(type); } return type; } } else if (types.length === 1) { let type = types[0]; switch (type.type) { case 'intrinsic': type = this.resolveIntrinsicType(type, ref); break; case 'reference': const newRef = Object.assign({}, ref, { type }); type = this.resolveExternalType(newRef); break; case 'reflection': type = this.resolveExternalType(type.declaration); break; } if (type !== undefined) { if (typeRef.types !== undefined && typeRef.types.length > 1) { return new GraphQLOptionalHelper(type); } return type; } } /* * Handles Enumeration when not implicit specified */ let enumType; let isEnum = true; for (const item of types) { if (item.type !== 'reference') { isEnum = false; break; } const type = this.getTypeDoc().get(types[0].id); if (type.kindString !== 'Enumeration member') { isEnum = false; break; } const parent = this.getTypeDoc().getParent(type.id); if (parent == null || (enumType != null && parent !== enumType)) { isEnum = false; break; } if (enumType == null) { enumType = parent; } } if (isEnum && enumType != null) { return this.resolveType(enumType); } throw this.relativeError('Union types must be an exported named type on its own file', ref); } resolveExternalType(ref, parents = [], isInput) { if (typeof ref.implementationOf !== 'undefined') { return this.resolveExternalType(Object.assign({}, ref, { type: ref.implementationOf, implementationOf: undefined }), parents, isInput); } if (ref.kindString !== undefined) { switch (ref.kindString) { case 'Constructor': return undefined; case 'Type literal': if (typeof ref.signatures !== 'undefined') { throw this.relativeError(`Function injection is not allowed, you should wrap it by specifying methods in a class`, ref); } // console.log('type literal', ref, parents); // Creates custom inputs by literals if (isInput || (parents.length > 0 && parents[parents.length - 1].kindString === 'Function')) { let name = ''; let { comment } = ref; for (const parent of [ref, ...parents]) { if (parents.length < 1 || parent !== parents[parents.length - 1]) { if (typeof parent.comment !== 'undefined') { ({ comment } = parent); } } if (parent.name === '__type') { continue; } name = `${capitalize_1.capitalize(parent.name)}${name}`; } let isInput = true; // TODO: Revise a better approach if (name === `${capitalize_1.capitalize(parents[1].name)}${capitalize_1.capitalize(parents[1].name)}` || name === `${capitalize_1.capitalize(parents[1].name)}Resolve` || name === `${capitalize_1.capitalize(parents[1].name)}Subscribe`) { name = `${capitalize_1.capitalize(parents[1].name)}Result`; isInput = false; } name = name.replace(/^(Resolve|Subscribe)/, ''); if (isInput) { name = `${name.replace(/Input/g, '')}Input`; return this.types[name] = this.resolveInterfaceType(Object.assign({}, ref, { comment, name }), isInput); } if (ref.kindString === 'Type literal') { return this.types[name] = this.resolveClassType(Object.assign({}, ref, { comment, name }), false); } return this.types[name] = this.resolveType(Object.assign({}, ref, { comment, name })); } throw this.relativeError(`Type literals are not allowed, you should export another named interface and reference it`, ref); case 'Method': if (ref.signatures !== undefined) { const signature = ref.signatures[0]; return this.resolveExternalType(signature, parents, isInput); } break; case 'Property': if (typeof ref.getSignature !== 'undefined') { return this.resolveExternalType(ref.getSignature, parents, isInput); } break; case 'Accessor': if (ref.getSignature === undefined) { return undefined; } return this.resolveExternalType(ref.getSignature, parents, isInput); } } if (ref.type !== undefined) { if (typeof ref.type.type === 'string') { switch (ref.type.type) { case 'reflection': if (ref.type.declaration !== undefined) { return this.resolveExternalType(ref.type.declaration, [ref, ...parents], isInput); } break; case 'intersection': // console.dir(ref.type); // console.dir(ref.overwrites); throw this.relativeError('Intersection is not yet supported', ref); case 'intrinsic': return this.resolveIntrinsicType(ref.type, ref); case 'union': return this.resolveExternalUnionType(ref.type, ref); case 'tuple': throw this.relativeError(`Tuples are not supported`, ref); case 'array': const newRef = Object.assign({}, ref, { type: ref.type.elementType }); const type = this.resolveExternalType(newRef, parents, isInput); if (type === undefined) { return type; } return new graphql_1.GraphQLList(type); case 'reference': const { name } = ref.type; if (name !== 'Connection' && this.hasType(name)) { return this.getType(name); } if (name === 'Integer') { return graphql_1.GraphQLInt; } if (name === 'ID') { return graphql_1.GraphQLID; } if (name === 'Date') { if (this.hasType('Timestamp')) { return this.getType('Timestamp'); } if (this.hasType('DateTime')) { return this.getType('DateTime'); } throw this.relativeError(`Could not resolve Date, you must declare a type uphanded named Date. Timestamp or DateTime`, ref); } if (typeof ref.type.id !== 'undefined') { const external = this.getTypeDoc().get(ref.type.id); if (typeof external.type !== 'undefined' && external.type.type === 'reference') { return this.resolveExternalType(external, parents, isInput); } if (isInput) { if (external.kindString === 'Interface') { let name = ''; let { comment } = ref; for (const parent of [external, ...parents]) { if (parents.length < 1 || parent !== parents[parents.length - 1]) { if (typeof parent.comment !== 'undefined') { ({ comment } = parent); } } if (parent.name === '__type') { continue; } name = `${capitalize_1.capitalize(parent.name)}${name}`; } name = `${name.replace(/Input/g, '')}Input`; return this.types[name] = this.resolveInterfaceType(Object.assign({}, external, { comment, name }), isInput); } throw this.relativeError(`Error`, external); } if (external.sources !== undefined) { const file = this.getTypeDoc().getParentExternalModule(external.id); if (file !== undefined) { const exportVar = this.getFileExport(file); if (exportVar === external) { return this.resolveType(external); } if (ref.kindString === 'Call signature') { let name = `${capitalize_1.capitalize(ref.name)}${capitalize_1.capitalize(external.name)}`; name = name.replace(/^(Resolve|Subscribe)/, ''); const newRef = Object.assign({}, external, { name }); return this.types[name] = this.resolveType(newRef, false); } if (external.kindString === 'Function') { if (typeof external.signatures === 'undefined') { throw this.relativeError(`Function ${external.name} lacks a call signature`, external); } return this.resolveExternalType(external.signatures[0], parents, isInput); } throw this.relativeError(`Referenced type ${external.name} must have their own file`, external); } } console.log(external); throw this.relativeError(`Could not resolve refereced type`, external); } if (ref.type.name === 'Array') { if (typeof ref.type.typeArguments !== 'undefined') { const newRef = Object.assign({}, ref, { type: ref.type.typeArguments[0] }); const type = this.resolveExternalType(newRef, parents, isInput); if (type === undefined) { return type; } return new graphql_1.GraphQLList(type); } } if (ref.type.name === 'Connection') { if (ref.type.typeArguments === undefined) { throw this.relativeError(`Could not resolve Connection without type arguments`, ref); } const innerTypeRef = ref.type.typeArguments[0]; const newRef = Object.assign({}, ref, { type: innerTypeRef }); const innerType = this.resolveExternalType(newRef, parents, isInput); const innerTypeClear = innerType instanceof graphql_1.GraphQLNonNull ? innerType.ofType : innerType; if (this.hasType(`${innerTypeClear.name}Connection`)) { return this.getType(`${innerTypeClear.name}Connection`); } if (!this.hasType(ref.type.name)) { throw this.relativeError('Cannot resolve Connection type as it is not implemented', ref); } if (!this.hasType('ConnectionEdge')) { throw this.relativeError('Cannot resolve ConnectionEdge as it is not implemented', ref); } const connectionEdge = this.getType('ConnectionEdge'); const connectionEdgeFields = {}; const connectionEdgeFieldsMap = connectionEdge.getFields(); for (const field in connectionEdgeFieldsMap) { const { type, description, args, } = connectionEdgeFieldsMap[field]; connectionEdgeFields[field] = { type, description, args: {}, }; for (const arg of args) { connectionEdgeFields[field].args[arg.name] = arg; } } const connectionEdgeName = `${innerTypeClear.name}ConnectionEdge`; const typeConnectionEdge = new graphql_1.GraphQLObjectType({ name: connectionEdgeName, // interfaces: [connectionEdge], description: connectionEdge.description, fields: Object.assign({}, connectionEdgeFields, { node: Object.assign({}, connectionEdgeFields.node, { type: innerType, args: {} }) }), }); this.types[connectionEdgeName] = typeConnectionEdge; const connection = this.getType(ref.type.name); const connectionFields = {}; const connectionFieldsMap = connection.getFields(); const connectionName = `${innerTypeClear.name}Connection`; for (const field in connectionFieldsMap) { const { type, description, args, } = connectionFieldsMap[field]; connectionFields[field] = { type, description, args: {}, }; for (const arg of args) { connectionFields[field].args[arg.name] = arg; } } this.types[connectionName] = new graphql_1.GraphQLObjectType({ name: connectionName, // interfaces: [connection], description: connection.description, fields: Object.assign({}, connectionFields, { edges: Object.assign({}, connectionFields.edges, { type: new graphql_1.GraphQLList(typeConnectionEdge), args: {} }), nodes: Object.assign({}, connectionFields.nodes, { type: new graphql_1.GraphQLList(innerType), args: {} }) }), }); return this.types[connectionName]; } for (const wrapper of ['Promise', 'AsyncIterator', 'AsyncIterableIterator', 'Observable']) { if (ref.type.name === wrapper) { if (ref.type.typeArguments === undefined) { throw this.relativeError(`Could not resolve ${wrapper} without type arguments`, ref); } const newRef = Object.assign({}, ref, { type: ref.type.typeArguments[0] }); return this.resolveExternalType(newRef, parents, isInput); } } if (ref.type.name === 'Resolver') { if (typeof ref.type.typeArguments !== 'undefined') { const newRef = Object.assign({}, ref, { type: ref.type.typeArguments[0] }); const external = this.resolveExternalType(newRef, parents, isInput); return external; } throw this.relativeError(`Resolver lacks typeArguments`, ref); } console.log(ref); throw this.relativeError(`Could not resolve external type ${name}, external types should be declared uphanded`, ref); } } } console.log(ref); throw this.relativeError(`Could not resolve unsupported type`, ref); } resolveInterfaceMethodArg(ref, parent, methodName) { /* TODO: default value { id: 15, name: 'args', kind: 32768, kindString: 'Parameter', flags: {}, type: [Object], defaultValue: ' { value: 1 }' } ], */ const args = {}; if (ref.kindString === 'Interface') { if (ref.children !== undefined) { for (const child of ref.children) { if (hasTag(child, 'graphql.disable')) { continue; } args[child.name] = this.resolveInterfacePropertyAsField(child, parent, true); } } return args; } const { type } = ref; if (type !== undefined) { switch (type.type) { case 'intrinsic': if (type.name === 'void') { return args; } break; case 'reference': if (type.id !== undefined) { return this.resolveInterfaceMethodArg(this.getTypeDoc().get(type.id), parent); } if (type.name === '__type') { return args; } if (type.name === 'ConnectionArguments') { return { first: { type: graphql_1.GraphQLInt, }, after: { type: graphql_1.GraphQLString, }, last: { type: graphql_1.GraphQLInt, }, before: { type: graphql_1.GraphQLString, }, }; } break; case 'reflection': if (type.declaration !== undefined) { if (type.declaration.children !== undefined) { for (const child of type.declaration.children) { args[child.name] = this.resolveInterfacePropertyAsField(child, parent, true); } } return args; } break; } } console.log(ref); throw this.relativeError('First parameter must be a type literal or interface to handle arguments', ref); } resolveInterfaceMethodArgs(ref, parent, methodName) { if (ref.parameters !== undefined) { const parameter = ref.parameters[0]; if (parameter.type !== undefined) { return this.resolveInterfaceMethodArg(parameter, parent || ref, methodName); } } } parseValueArg(arg) { switch (arg) { case 'true': case 'false': return Boolean(arg); case 'null': return null; default: if (/^\d+$/.test(arg)) { return parseInt(arg, 10); } if (/^\d+\.\d+$/.test(arg)) { return parseFloat(arg); } return arg; } } parseValueJSON(tagText) { const value = tagText.replace(/\\n/g, ''); return JSON.parse(value); } parseValue(tagText) { const match = tagText.replace(/\\n/g, '').match(/\w+|"(?:\\"|[^"])+"/g); if (match === null) { return undefined; } const [name, ...args] = match.map(value => value.replace(/^"(.*)"$/, '$1')); return { name, args: args.map(this.parseValueArg) }; } resolveInterfacePropertyAsField(ref, parent, isInput, methodName) { if (ref.kindString === 'Method') { if (ref.signatures === undefined) { throw this.relativeError('Could not process method signature', ref); } let newRef; if (typeof ref.implementationOf !== 'undefined' && typeof ref.signatures !== 'undefined' && (typeof ref.type !== 'undefined' && ref.type.type === 'union')) { const signatures = ref.signatures.slice(0); signatures[0] = Object.assign({}, signatures[0], { type: ref.implementationOf }); newRef = Object.assign({}, signatures[0], { type: ref.implementationOf, comment: ref.comment }); } else { newRef = Object.assign({}, ref.signatures[0], { comment: ref.comment }); } return this.resolveInterfacePropertyAsField(newRef, parent, isInput, methodName); } if (methodName === 'Subscription') { if (ref.type === undefined || ref.type.type !== 'reference' || ['AsyncIterator', 'AsyncIterableIterator', 'Observable'].indexOf(ref.type.name) < 0) { throw this.relativeError('Subscription methods must return AsyncIterator or Observable', ref); } } let type = this.resolveExternalType(ref, [parent], isInput); if (type === undefined) { return type; } if (ref.flags === undefined || ref.flags.isOptional !== true) { if (!(type instanceof GraphQLOptionalHelper)) { type = new graphql_1.GraphQLNonNull(type); } } if (type instanceof GraphQLOptionalHelper) { type = type.getType(); } if (hasTag(ref, 'graphql.nullable')) { if (type instanceof graphql_1.GraphQLNonNull) { type = type.ofType; } } const config = { type }; if (ref.kindString === 'Call signature') { const args = this.resolveInterfaceMethodArgs(ref, parent, methodName); if (args !== undefined) { config.args = args; } } if (ref.comment !== undefined) { if (ref.comment.shortText !== undefined) { config.description = ref.comment.shortText.replace("'", "\\'"); } let defaultValue = getTag(ref, 'graphql.default'); if (typeof defaultValue === 'string') { if (!(type instanceof graphql_1.GraphQLNonNull)) { throw this.relativeError(`Field ${ref.name} must be required as it has @GraphQL.default set`, ref); } type = type.ofType; config.type = type; try { defaultValue = this.parseValueJSON(defaultValue); } catch (err) { throw this.relativeError(`Couldn't parse @Graphql.default value as JSON`, ref); } try { config.defaultValue = type.parseValue(defaultValue); } catch (error) { throw this.relativeError(`Couldn't parse @Graphql.default to its type`, ref); } } const sanitize = []; const validate = []; const parseValidators = (ref) => { let isValidating = false; if (ref.comment !== undefined && ref.comment.tags !== undefined) { for (const { tag, text } of ref.comment.tags) { if (tag === 'graphql.sanitize') { if (isValidating) { throw this.relativeError('@GraphQL.sanitize must come before @GraphQL.validate', ref); } const parser = this.parseValue(text); if (parser === undefined) { throw this.relativeError('@GraphQL.sanitize must have a method name declared', ref); } if (this.parserHandler === undefined) { throw this.relativeError('@GraphQL.sanitize is not available, define a parseHandler', ref); } if (parser !== undefined) { try { this.parserHandler.test('sanitize', type, parser.name, parser.args); } catch (error) { throw this.relativeError(error.message, ref); } sanitize.push(parser); } } else if (tag === 'graphql.validate') { isValidating = true; const parser = this.parseValue(text); if (parser === undefined) { throw this.relativeError('@GraphQL.validate must have a method name declared', ref); } if (this.parserHandler === undefined) { throw this.relativeError('@GraphQL.validate is not available, define a parseHandler', ref); } if (parser !== undefined) { try { this.parserHandler.test('validate', type, parser.name, parser.args); } catch (error) { throw this.relativeError(error.message, ref); } validate.push(parser); } } } } }; if (ref.type !== undefined && ref.type.type === 'reference') { const typeRefId = ref.type.id; if (typeRefId !== undefined) { parseValidators(this.getTypeDoc().get(typeRefId)); } } parseValidators(ref); if (sanitize.length > 0) { config.sanitize = sanitize; } if (validate.length > 0) { config.validate = validate; } } return config; } resolveInterfaceType(ref, isInput) { const { name } = ref; const config = { name }; if (ref.comment !== undefined && ref.comment.shortText !== undefined) { config.description = ref.comment.shortText.replace("'", "\\'"); } let resolvedFields; const resolveFields = () => { resolvedFields = this.resolveFields(ref, isInput); return resolvedFields; }; config.fields = resolveFields; if (typeof isInput === undefined) { isInput = false; if (ref.extendedTypes !== undefined) { for (const extendedType of ref.extendedTypes) { if (extendedType.name === 'Input') { isInput = true; break; } } } } let type; if (isInput) { type = new graphql_1.GraphQLInputObjectType(config); injectCustomAttributes(type, () => resolvedFields); } else { type = new graphql_1.GraphQLInterfaceType(config); } return type; } resolveFields(ref, isInput) { const resolvedFields = {}; const getChildren = () => { const children = {}; if (typeof ref.extendedTypes !== 'undefined') { for (let extendedType of ref.extendedTypes) { if (extendedType.type === 'intersection') { if (typeof extendedType.types !== 'undefined') { for (let type of extendedType.types) { if (type.type !== 'reference' || typeof type.id === 'undefined') { continue; } const extendedClass = this.typedoc.get(type.id); if (typeof extendedClass.children === 'undefined') { continue; } for (const child of extendedClass.children) { if (child.flags.isPrivate === true || child.flags.isProtected === true || child.flags.isStatic === true) { continue; } children[child.name] = child; } } } } } } if (typeof ref.children !== 'undefined') { for (const child of ref.children) { if (child.flags.isPrivate === true || child.flags.isProtected === true || child.flags.isStatic === true) { continue; } const previous = children[child.name]; let current = child; if (typeof previous !== 'undefined' && typeof previous.comment !== 'undefined') { if (typeof current.comment === 'undefined' || typeof current.comment.shortText === 'undefined') { current = Object.assign({}, current, previous, { flags: current.flags }); current.comment = Object.assign({}, previous.comment, current.comment); } } children[child.name] = current; } } return Object.values(children); }; const children = getChildren(); for (let child of children) { if (hasTag(child, 'graphql.disable')) { continue; } if (['isTypeOf', 'toGraphQL'].indexOf(child.name) >= 0) { continue; } if (child.kindString === 'Method' || (typeof child.type !== 'undefined' && child.type.type === 'reference' && child.type.name === 'Resolver')) { const match = child.name.match(/^resolve([A-Z].+)$/); if (match == null) { continue; } let field = this.resolveInterfacePropertyAsField(child, ref); let fieldType = field.type; let fieldRequired = false; if (fieldType instanceof graphql_1.GraphQLNonNull) { fieldType = fieldType.ofType; fieldRequired = true; } let fieldList = false; if (fieldType instanceof graphql_1.GraphQLList) { fieldType = fieldType.ofType; fieldList = true; } let hasField = false; const fieldName = lowerFirst_1.lowerFirst(match[1]); for (const matchChild of children) { if (matchChild.name === fieldName) { hasField = true; const matchField = this.resolveInterfacePropertyAsField(matchChild, ref); if (typeof field.description === 'undefined') { field.description = matchField.description; } let matchFieldType = matchField.type; let matchFieldRequired = false; if (matchFieldType instanceof graphql_1.GraphQLNonNull) { matchFieldType = matchFieldType.ofType; matchFieldRequired = true; } let matchFieldList = false; if (matchFieldType instanceof graphql_1.GraphQLList) { matchFieldType = matchFieldType.ofType; matchFieldList = true; } if (fieldType !== matchFieldType || matchFieldList !== fieldList) { throw this.relativeError(`The field ${fieldName} must match the resolve method`, child); } if (matchFieldRequired && !fieldRequired) { throw this.relativeError(`A resolved field ${fieldName} must be optional`, matchChild); } child = Object.assign({}, child, { name: fieldName }); if (typeof matchChild.comment !== 'undefined') { if (typeof child.comment === 'undefined') { child.comment = Object.assign({}, matchChild.comment); } else { child.comment = Object.assign({}, child.comment); if (typeof matchChild.comment.shortText !== 'undefined') { child.comment.shortText = matchChild.comment.shortText.replace("'", "\\'"); } if (typeof matchChild.comment.tags !== 'undefined') { if (typeof child.comment.tags === 'undefined') { child.comment.tags = []; } child.comment.tags = [ ...child.comment.tags, ...matchChild.comment.tags, ]; } } } field = this.resolveInterfacePropertyAsField(child, ref); } } if (!hasField) { throw this.relativeError(`A field name ${fieldName} must be defined to be compliant to resolver`, child); } } if (!isInput) { if (hasTag(child, 'graphql.validate')) { throw this.relativeError('@GraphQL.validate tag only works on interface fields that are gonna be InputObjectType or arguments of ObjectType methods', child); } if (hasTag(child, 'graphql.sanitize')) { throw this.relativeError('@GraphQL.sanitize tag only works on interface fields that are gonna be InputObjectType or arguments of ObjectType methods', child); } } if (typeof ref.children !== 'undefined') { for (const matchChild of ref.children) { if (`resolve${capitalize_1.capitalize(child.name)}` === matchChild.name) { continue; } } } const field = this.resolveInterfacePropertyAsField(child, ref, isInput); if (field === undefined) { continue; } resolvedFields[child.name] = field; } return resolvedFields; } resolveClassType(ref, checkExtendObjectType = false) { const { name } = ref; const config = { name }; if (ref.comment !== undefined && ref.comment.shortText !== undefined) { config.description = ref.comment.shortText.replace("'", "\\'"); } if (checkExtendObjectType) { let extendObjectType = false; if (typeof ref.extendedTypes !== 'undefined') { for (const extendedType of ref.extendedTypes) { if (extendedType.type === 'intersection') { if (typeof extendedType.types !== 'undefined') { for (const type of extendedType.types) { if (type.type === 'reference' && type.name === 'ObjectType') { if (typeof type.typeArguments === 'undefined') { break; } const typeArg = type.typeArguments[0]; for (const extendedTypeOf of extendedType.types) { if (extendedTypeOf.name === typeArg.name) { extendObjectType = true; } } } } } } } } if (!extendObjectType) { throw this.relativeError(`The ${ref.name} class must extend ObjectType`, ref); } } let resolvedFields; const resolveFields = () => { resolvedFields = this.resolveFields(ref); return resolvedFields; }; config.interfaces = () => { const interfaces = []; const implementedTypesMap = {}; const findExtendedTypes = (ref) => { if (ref.extendedTypes !== undefined) { for (const extendedType of ref.extendedTypes) { const interfaceType = this.resolveExternalType(Object.assign({}, ref, { type: extendedType })); if (interfaceType instanceof graphql_1.GraphQLInterfaceType) { implementedTypesMap[extendedType.name] = interfaceType; } if (extendedType.id !== undefined) { findExtendedTypes(this.typedoc.get(extendedType.id)); } } } }; const findImplementedTypes = (ref) => { if (ref.implementedTypes !== undefined) { for (const implementedType of ref.implementedTypes) { const interfaceType = this.resolveExternalType(Object.assign({}, ref, { type: implementedType })); if (interfaceType instanceof graphql_1.GraphQLInterfaceType) { implementedTypesMap[implementedType.name] = interfaceType; } if (implementedType.id !== undefined) { findExtendedTypes(this.typedoc.get(implementedType.id)); } } } }; findImplementedTypes(ref); for (const typeName in implementedTypesMap) { interfaces.push(implementedTypesMap[typeName]); } return interfaces; }; config.fields = resolveFields; const type = new graphql_1.GraphQLObjectType(config); injectCustomAttributes(type, () => resolvedFields); return type; } resolveEnumerationType(ref) { const { name } = ref; const values = {}; const config = { name, values }; if (ref.comment !== undefined && ref.comment.shortText !== undefined) { config.description = ref.comment.shortText.replace("'", "\\'"); } if (ref.children !== undefined) { let nextValue = 0; const valuesOrder = []; function compare(a, b) { if (a.sources[0].line < b.sources[0].line) return -1; if (a.sources[0]