@cortexql/ts2graphql
Version:
A TypeScrpt transpiler to GraphQL for your project
1,034 lines • 62 kB
JavaScript
"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]