diff --git a/src/type/definition.js b/src/type/definition.js index c4c01efadb..3da635c113 100644 --- a/src/type/definition.js +++ b/src/type/definition.js @@ -14,6 +14,7 @@ import instanceOf from '../jsutils/instanceOf'; import inspect from '../jsutils/inspect'; import invariant from '../jsutils/invariant'; import keyMap from '../jsutils/keyMap'; +import keyValMap from '../jsutils/keyValMap'; import mapValue from '../jsutils/mapValue'; import type { ObjMap } from '../jsutils/ObjMap'; import { Kind } from '../language/kinds'; @@ -510,6 +511,10 @@ function resolveThunk<+T>(thunk: Thunk): T { return typeof thunk === 'function' ? thunk() : thunk; } +function undefineIfEmpty(arr: ?$ReadOnlyArray): ?$ReadOnlyArray { + return arr && arr.length > 0 ? arr : undefined; +} + /** * Scalar Type Definition * @@ -550,7 +555,7 @@ export class GraphQLScalarType { this.parseValue = config.parseValue || (value => value); this.parseLiteral = config.parseLiteral || valueFromASTUntyped; this.astNode = config.astNode; - this.extensionASTNodes = config.extensionASTNodes; + this.extensionASTNodes = undefineIfEmpty(config.extensionASTNodes); invariant(typeof config.name === 'string', 'Must provide name.'); invariant( typeof config.serialize === 'function', @@ -568,6 +573,23 @@ export class GraphQLScalarType { } } + toConfig(): {| + ...GraphQLScalarTypeConfig<*, *>, + parseValue: GraphQLScalarValueParser<*>, + parseLiteral: GraphQLScalarLiteralParser<*>, + extensionASTNodes: $ReadOnlyArray, + |} { + return { + name: this.name, + description: this.description, + serialize: this.serialize, + parseValue: this.parseValue, + parseLiteral: this.parseLiteral, + astNode: this.astNode, + extensionASTNodes: this.extensionASTNodes || [], + }; + } + toString(): string { return this.name; } @@ -648,7 +670,7 @@ export class GraphQLObjectType { this.name = config.name; this.description = config.description; this.astNode = config.astNode; - this.extensionASTNodes = config.extensionASTNodes; + this.extensionASTNodes = undefineIfEmpty(config.extensionASTNodes); this.isTypeOf = config.isTypeOf; this._fields = defineFieldMap.bind(undefined, config); this._interfaces = defineInterfaces.bind(undefined, config); @@ -674,6 +696,23 @@ export class GraphQLObjectType { return this._interfaces; } + toConfig(): {| + ...GraphQLObjectTypeConfig<*, *>, + interfaces: Array, + fields: GraphQLFieldConfigMap<*, *>, + extensionASTNodes: $ReadOnlyArray, + |} { + return { + name: this.name, + description: this.description, + isTypeOf: this.isTypeOf, + interfaces: this.getInterfaces(), + fields: fieldsToFieldsConfig(this.getFields()), + astNode: this.astNode, + extensionASTNodes: this.extensionASTNodes || [], + }; + } + toString(): string { return this.name; } @@ -751,6 +790,33 @@ function isPlainObj(obj) { return obj && typeof obj === 'object' && !Array.isArray(obj); } +function fieldsToFieldsConfig(fields) { + return mapValue(fields, field => ({ + type: field.type, + args: argsToArgsConfig(field.args), + resolve: field.resolve, + subscribe: field.subscribe, + deprecationReason: field.deprecationReason, + description: field.description, + astNode: field.astNode, + })); +} + +export function argsToArgsConfig( + args: Array, +): GraphQLFieldConfigArgumentMap { + return keyValMap( + args, + arg => arg.name, + arg => ({ + type: arg.type, + defaultValue: arg.defaultValue, + description: arg.description, + astNode: arg.astNode, + }), + ); +} + export type GraphQLObjectTypeConfig = {| name: string, interfaces?: Thunk>, @@ -892,7 +958,7 @@ export class GraphQLInterfaceType { this.name = config.name; this.description = config.description; this.astNode = config.astNode; - this.extensionASTNodes = config.extensionASTNodes; + this.extensionASTNodes = undefineIfEmpty(config.extensionASTNodes); this.resolveType = config.resolveType; this._fields = defineFieldMap.bind(undefined, config); invariant(typeof config.name === 'string', 'Must provide name.'); @@ -910,6 +976,21 @@ export class GraphQLInterfaceType { return this._fields; } + toConfig(): {| + ...GraphQLInterfaceTypeConfig<*, *>, + fields: GraphQLFieldConfigMap<*, *>, + extensionASTNodes: $ReadOnlyArray, + |} { + return { + name: this.name, + description: this.description, + resolveType: this.resolveType, + fields: fieldsToFieldsConfig(this.getFields()), + astNode: this.astNode, + extensionASTNodes: this.extensionASTNodes || [], + }; + } + toString(): string { return this.name; } @@ -969,7 +1050,7 @@ export class GraphQLUnionType { this.name = config.name; this.description = config.description; this.astNode = config.astNode; - this.extensionASTNodes = config.extensionASTNodes; + this.extensionASTNodes = undefineIfEmpty(config.extensionASTNodes); this.resolveType = config.resolveType; this._types = defineTypes.bind(undefined, config); invariant(typeof config.name === 'string', 'Must provide name.'); @@ -987,6 +1068,21 @@ export class GraphQLUnionType { return this._types; } + toConfig(): {| + ...GraphQLUnionTypeConfig<*, *>, + types: Array, + extensionASTNodes: $ReadOnlyArray, + |} { + return { + name: this.name, + description: this.description, + resolveType: this.resolveType, + types: this.getTypes(), + astNode: this.astNode, + extensionASTNodes: this.extensionASTNodes || [], + }; + } + toString(): string { return this.name; } @@ -1057,7 +1153,7 @@ export class GraphQLEnumType /* */ { this.name = config.name; this.description = config.description; this.astNode = config.astNode; - this.extensionASTNodes = config.extensionASTNodes; + this.extensionASTNodes = undefineIfEmpty(config.extensionASTNodes); this._values = defineEnumValues(this, config.values); this._valueLookup = new Map( this._values.map(enumValue => [enumValue.value, enumValue]), @@ -1101,6 +1197,30 @@ export class GraphQLEnumType /* */ { } } + toConfig(): {| + ...GraphQLEnumTypeConfig, + extensionASTNodes: $ReadOnlyArray, + |} { + const values = keyValMap( + this.getValues(), + value => value.name, + value => ({ + description: value.description, + value: value.value, + deprecationReason: value.deprecationReason, + astNode: value.astNode, + }), + ); + + return { + name: this.name, + description: this.description, + values, + astNode: this.astNode, + extensionASTNodes: this.extensionASTNodes || [], + }; + } + toString(): string { return this.name; } @@ -1198,7 +1318,7 @@ export class GraphQLInputObjectType { this.name = config.name; this.description = config.description; this.astNode = config.astNode; - this.extensionASTNodes = config.extensionASTNodes; + this.extensionASTNodes = undefineIfEmpty(config.extensionASTNodes); this._fields = defineInputFieldMap.bind(undefined, config); invariant(typeof config.name === 'string', 'Must provide name.'); } @@ -1210,6 +1330,27 @@ export class GraphQLInputObjectType { return this._fields; } + toConfig(): {| + ...GraphQLInputObjectTypeConfig, + fields: GraphQLInputFieldConfigMap, + extensionASTNodes: $ReadOnlyArray, + |} { + const fields = mapValue(this.getFields(), field => ({ + description: field.description, + type: field.type, + defaultValue: field.defaultValue, + astNode: field.astNode, + })); + + return { + name: this.name, + description: this.description, + fields, + astNode: this.astNode, + extensionASTNodes: this.extensionASTNodes || [], + }; + } + toString(): string { return this.name; } diff --git a/src/type/directives.js b/src/type/directives.js index 747bbda56c..97e824d290 100644 --- a/src/type/directives.js +++ b/src/type/directives.js @@ -8,6 +8,7 @@ */ import objectEntries from '../polyfills/objectEntries'; +import { argsToArgsConfig } from './definition'; import type { GraphQLFieldConfigArgumentMap, GraphQLArgument, @@ -84,6 +85,19 @@ export class GraphQLDirective { toString(): string { return '@' + this.name; } + + toConfig(): {| + ...GraphQLDirectiveConfig, + args: GraphQLFieldConfigArgumentMap, + |} { + return { + name: this.name, + description: this.description, + locations: this.locations, + args: argsToArgsConfig(this.args), + astNode: this.astNode, + }; + } } // Conditionally apply `[Symbol.toStringTag]` if `Symbol`s are supported diff --git a/src/type/schema.js b/src/type/schema.js index 006211e823..e953e7d00a 100644 --- a/src/type/schema.js +++ b/src/type/schema.js @@ -238,6 +238,27 @@ export class GraphQLSchema { getDirective(name: string): ?GraphQLDirective { return find(this.getDirectives(), directive => directive.name === name); } + + toConfig(): {| + ...GraphQLSchemaConfig, + types: Array, + directives: Array, + extensionASTNodes: $ReadOnlyArray, + assumeValid: boolean, + allowedLegacyNames: $ReadOnlyArray, + |} { + return { + types: objectValues(this.getTypeMap()), + directives: this.getDirectives().slice(), + query: this.getQueryType(), + mutation: this.getMutationType(), + subscription: this.getSubscriptionType(), + astNode: this.astNode, + extensionASTNodes: this.extensionASTNodes || [], + assumeValid: this.__validationErrors !== undefined, + allowedLegacyNames: this.__allowedLegacyNames, + }; + } } // Conditionally apply `[Symbol.toStringTag]` if `Symbol`s are supported diff --git a/src/utilities/extendSchema.js b/src/utilities/extendSchema.js index f34efe2248..00e168bc7c 100644 --- a/src/utilities/extendSchema.js +++ b/src/utilities/extendSchema.js @@ -10,7 +10,6 @@ import objectValues from '../polyfills/objectValues'; import invariant from '../jsutils/invariant'; import mapValue from '../jsutils/mapValue'; -import keyValMap from '../jsutils/keyValMap'; import { ASTDefinitionBuilder } from './buildASTSchema'; import { assertValidSDLExtension } from '../validation/validate'; import { assertSchema, GraphQLSchema } from '../type/schema'; @@ -18,13 +17,7 @@ import { isIntrospectionType } from '../type/introspection'; import { isSpecifiedScalarType } from '../type/scalars'; import type { GraphQLSchemaValidationOptions } from '../type/schema'; - -import type { - GraphQLType, - GraphQLNamedType, - GraphQLArgument, - GraphQLFieldConfigArgumentMap, -} from '../type/definition'; +import type { GraphQLType, GraphQLNamedType } from '../type/definition'; import { isScalarType, @@ -170,12 +163,13 @@ export function extendSchema( ); const extendTypeCache = Object.create(null); + const schemaConfig = schema.toConfig(); // Get the extended root operation types. const operationTypes = { - query: extendMaybeNamedType(schema.getQueryType()), - mutation: extendMaybeNamedType(schema.getMutationType()), - subscription: extendMaybeNamedType(schema.getSubscriptionType()), + query: extendMaybeNamedType(schemaConfig.query), + mutation: extendMaybeNamedType(schemaConfig.mutation), + subscription: extendMaybeNamedType(schemaConfig.subscription), }; if (schemaDef) { @@ -198,32 +192,26 @@ export function extendSchema( } } - const schemaExtensionASTNodes = schemaExtensions - ? schema.extensionASTNodes - ? schema.extensionASTNodes.concat(schemaExtensions) - : schemaExtensions - : schema.extensionASTNodes; - - const types = [ + const schemaTypes = [ // Iterate through all types, getting the type definition for each, ensuring // that any type not directly referenced by a field will get created. - ...objectValues(schema.getTypeMap()).map(type => extendNamedType(type)), + ...schemaConfig.types.map(type => extendNamedType(type)), // Do the same with new types. ...objectValues(typeDefinitionMap).map(type => astBuilder.buildType(type)), ]; // Support both original legacy names and extended legacy names. - const allowedLegacyNames = schema.__allowedLegacyNames.concat( + const allowedLegacyNames = schemaConfig.allowedLegacyNames.concat( (options && options.allowedLegacyNames) || [], ); // Then produce and return a Schema with these types. return new GraphQLSchema({ ...operationTypes, - types, + types: schemaTypes, directives: getMergedDirectives(), - astNode: schema.astNode, - extensionASTNodes: schemaExtensionASTNodes, + astNode: schemaConfig.astNode, + extensionASTNodes: schemaConfig.extensionASTNodes.concat(schemaExtensions), allowedLegacyNames, }); @@ -269,243 +257,160 @@ export function extendSchema( } function extendDirective(directive: GraphQLDirective): GraphQLDirective { + const config = directive.toConfig(); + return new GraphQLDirective({ - name: directive.name, - description: directive.description, - locations: directive.locations, - args: extendArgs(directive.args), - astNode: directive.astNode, + ...config, + args: extendArgs(config.args), }); } function extendInputObjectType( type: GraphQLInputObjectType, ): GraphQLInputObjectType { - const name = type.name; - const extensionASTNodes = typeExtensionsMap[name] - ? type.extensionASTNodes - ? type.extensionASTNodes.concat(typeExtensionsMap[name]) - : typeExtensionsMap[name] - : type.extensionASTNodes; + const config = type.toConfig(); + const extensionNodes = typeExtensionsMap[config.name] || []; + return new GraphQLInputObjectType({ - name, - description: type.description, - fields: () => extendInputFieldMap(type), - astNode: type.astNode, - extensionASTNodes, + ...config, + fields: fieldsThunk, + extensionASTNodes: config.extensionASTNodes.concat(extensionNodes), }); - } - function extendInputFieldMap(type: GraphQLInputObjectType) { - const newFieldMap = mapValue(type.getFields(), field => ({ - description: field.description, - type: extendType(field.type), - defaultValue: field.defaultValue, - astNode: field.astNode, - })); + function fieldsThunk() { + const fields = mapValue(config.fields, field => ({ + ...field, + type: extendType(field.type), + })); - // If there are any extensions to the fields, apply those here. - const extensions = typeExtensionsMap[type.name]; - if (extensions) { - for (const extension of extensions) { + // If there are any extensions to the fields, apply those here. + for (const extension of extensionNodes) { for (const field of extension.fields) { - newFieldMap[field.name.value] = astBuilder.buildInputField(field); + fields[field.name.value] = astBuilder.buildInputField(field); } } + return fields; } - - return newFieldMap; } function extendEnumType(type: GraphQLEnumType): GraphQLEnumType { - const name = type.name; - const extensionASTNodes = typeExtensionsMap[name] - ? type.extensionASTNodes - ? type.extensionASTNodes.concat(typeExtensionsMap[name]) - : typeExtensionsMap[name] - : type.extensionASTNodes; - return new GraphQLEnumType({ - name, - description: type.description, - values: extendValueMap(type), - astNode: type.astNode, - extensionASTNodes, - }); - } - - function extendValueMap(type: GraphQLEnumType) { - const newValueMap = Object.create(null); - for (const value of type.getValues()) { - newValueMap[value.name] = { - description: value.description, - value: value.value, - deprecationReason: value.deprecationReason, - astNode: value.astNode, - }; - } + const config = type.toConfig(); + const extensionNodes = typeExtensionsMap[type.name] || []; + const values = config.values; // If there are any extensions to the values, apply those here. - const extensions = typeExtensionsMap[type.name]; - if (extensions) { - for (const extension of extensions) { - for (const value of extension.values) { - newValueMap[value.name.value] = astBuilder.buildEnumValue(value); - } + for (const extension of extensionNodes) { + for (const value of extension.values) { + values[value.name.value] = astBuilder.buildEnumValue(value); } } - return newValueMap; + return new GraphQLEnumType({ + ...config, + values, + extensionASTNodes: config.extensionASTNodes.concat(extensionNodes), + }); } function extendScalarType(type: GraphQLScalarType): GraphQLScalarType { - const name = type.name; - const extensionASTNodes = typeExtensionsMap[name] - ? type.extensionASTNodes - ? type.extensionASTNodes.concat(typeExtensionsMap[name]) - : typeExtensionsMap[name] - : type.extensionASTNodes; + const config = type.toConfig(); + const extensionNodes = typeExtensionsMap[config.name] || []; + return new GraphQLScalarType({ - name, - description: type.description, - astNode: type.astNode, - extensionASTNodes, - serialize: type.serialize, - parseValue: type.parseValue, - parseLiteral: type.parseLiteral, + ...config, + extensionASTNodes: config.extensionASTNodes.concat(extensionNodes), }); } function extendObjectType(type: GraphQLObjectType): GraphQLObjectType { - const name = type.name; - const extensionASTNodes = typeExtensionsMap[name] - ? type.extensionASTNodes - ? type.extensionASTNodes.concat(typeExtensionsMap[name]) - : typeExtensionsMap[name] - : type.extensionASTNodes; + const config = type.toConfig(); + const extensionNodes = typeExtensionsMap[config.name] || []; + return new GraphQLObjectType({ - name, - description: type.description, - interfaces: () => extendImplementedInterfaces(type), - fields: () => extendFieldMap(type), - astNode: type.astNode, - extensionASTNodes, - isTypeOf: type.isTypeOf, + ...config, + interfaces: interfacesThunk, + fields: () => extendFieldMap(config), + extensionASTNodes: config.extensionASTNodes.concat(extensionNodes), }); - } - function extendArgs( - args: Array, - ): GraphQLFieldConfigArgumentMap { - return keyValMap( - args, - arg => arg.name, - arg => ({ - type: extendType(arg.type), - defaultValue: arg.defaultValue, - description: arg.description, - astNode: arg.astNode, - }), - ); + function interfacesThunk() { + const interfaces = config.interfaces.map(extendNamedType); + + // If there are any extensions to the interfaces, apply those here. + for (const extension of extensionNodes) { + for (const namedType of extension.interfaces) { + // Note: While this could make early assertions to get the correctly + // typed values, that would throw immediately while type system + // validation with validateSchema() will produce more actionable results. + interfaces.push((astBuilder.buildType(namedType): any)); + } + } + return interfaces; + } } function extendInterfaceType( type: GraphQLInterfaceType, ): GraphQLInterfaceType { - const name = type.name; - const extensionASTNodes = typeExtensionsMap[name] - ? type.extensionASTNodes - ? type.extensionASTNodes.concat(typeExtensionsMap[name]) - : typeExtensionsMap[name] - : type.extensionASTNodes; + const config = type.toConfig(); + const extensionNodes = typeExtensionsMap[config.name] || []; + return new GraphQLInterfaceType({ - name: type.name, - description: type.description, - fields: () => extendFieldMap(type), - astNode: type.astNode, - extensionASTNodes, - resolveType: type.resolveType, + ...config, + fields: () => extendFieldMap(config), + extensionASTNodes: config.extensionASTNodes.concat(extensionNodes), }); } function extendUnionType(type: GraphQLUnionType): GraphQLUnionType { - const name = type.name; - const extensionASTNodes = typeExtensionsMap[name] - ? type.extensionASTNodes - ? type.extensionASTNodes.concat(typeExtensionsMap[name]) - : typeExtensionsMap[name] - : type.extensionASTNodes; + const config = type.toConfig(); + const extensionNodes = typeExtensionsMap[config.name] || []; + return new GraphQLUnionType({ - name, - description: type.description, - types: () => extendPossibleTypes(type), - astNode: type.astNode, - resolveType: type.resolveType, - extensionASTNodes, + ...config, + types: typesThunk, + extensionASTNodes: config.extensionASTNodes.concat(extensionNodes), }); - } - function extendPossibleTypes( - type: GraphQLUnionType, - ): Array { - const possibleTypes = type.getTypes().map(extendNamedType); + function typesThunk() { + const types = config.types.map(extendNamedType); - // If there are any extensions to the union, apply those here. - const extensions = typeExtensionsMap[type.name]; - if (extensions) { - for (const extension of extensions) { + // If there are any extensions to the union, apply those here. + for (const extension of extensionNodes) { for (const namedType of extension.types) { // Note: While this could make early assertions to get the correctly // typed values, that would throw immediately while type system // validation with validateSchema() will produce more actionable results. - possibleTypes.push((astBuilder.buildType(namedType): any)); + types.push((astBuilder.buildType(namedType): any)); } } + return types; } - return possibleTypes; } - function extendImplementedInterfaces( - type: GraphQLObjectType, - ): Array { - const interfaces = type.getInterfaces().map(extendNamedType); - - // If there are any extensions to the interfaces, apply those here. - const extensions = typeExtensionsMap[type.name]; - if (extensions) { - for (const extension of extensions) { - for (const namedType of extension.interfaces) { - // Note: While this could make early assertions to get the correctly - // typed values, that would throw immediately while type system - // validation with validateSchema() will produce more actionable results. - interfaces.push((astBuilder.buildType(namedType): any)); - } - } - } - - return interfaces; + function extendArgs(oldArgs) { + return mapValue(oldArgs, arg => ({ + ...arg, + type: extendType(arg.type), + })); } - function extendFieldMap(type: GraphQLObjectType | GraphQLInterfaceType) { - const newFieldMap = mapValue(type.getFields(), field => ({ - description: field.description, - deprecationReason: field.deprecationReason, + function extendFieldMap(config) { + const fields = mapValue(config.fields, field => ({ + ...field, type: extendType(field.type), args: extendArgs(field.args), - astNode: field.astNode, - resolve: field.resolve, })); // If there are any extensions to the fields, apply those here. - const extensions = typeExtensionsMap[type.name]; - if (extensions) { - for (const extension of extensions) { - for (const field of extension.fields) { - newFieldMap[field.name.value] = astBuilder.buildField(field); - } + const extensionNodes = typeExtensionsMap[config.name] || []; + for (const extension of extensionNodes) { + for (const field of extension.fields) { + fields[field.name.value] = astBuilder.buildField(field); } } - return newFieldMap; + return fields; } function extendType(typeDef: T): T { diff --git a/src/utilities/lexicographicSortSchema.js b/src/utilities/lexicographicSortSchema.js index 14698bdc08..0260669826 100644 --- a/src/utilities/lexicographicSortSchema.js +++ b/src/utilities/lexicographicSortSchema.js @@ -7,9 +7,7 @@ * @flow strict */ -import objectValues from '../polyfills/objectValues'; import type { ObjMap } from '../jsutils/ObjMap'; -import keyValMap from '../jsutils/keyValMap'; import { GraphQLSchema } from '../type/schema'; import { GraphQLDirective } from '../type/directives'; import type { GraphQLNamedType } from '../type/definition'; @@ -39,55 +37,45 @@ import { isIntrospectionType } from '../type/introspection'; export function lexicographicSortSchema(schema: GraphQLSchema): GraphQLSchema { const cache = Object.create(null); + const schemaConfig = schema.toConfig(); const sortMaybeType = maybeType => maybeType && sortNamedType(maybeType); return new GraphQLSchema({ - types: sortTypes(objectValues(schema.getTypeMap())), - directives: sortByName(schema.getDirectives()).map(sortDirective), - query: sortMaybeType(schema.getQueryType()), - mutation: sortMaybeType(schema.getMutationType()), - subscription: sortMaybeType(schema.getSubscriptionType()), - astNode: schema.astNode, + ...schemaConfig, + types: sortTypes(schemaConfig.types), + directives: sortByName(schemaConfig.directives).map(sortDirective), + query: sortMaybeType(schemaConfig.query), + mutation: sortMaybeType(schemaConfig.mutation), + subscription: sortMaybeType(schemaConfig.subscription), }); function sortDirective(directive) { + const config = directive.toConfig(); return new GraphQLDirective({ - name: directive.name, - description: directive.description, - locations: sortBy(directive.locations, x => x), - args: sortArgs(directive.args), - astNode: directive.astNode, + ...config, + locations: sortBy(config.locations, x => x), + args: sortArgs(config.args), }); } function sortArgs(args) { - return keyValMap( - sortByName(args), - arg => arg.name, - arg => ({ - ...arg, - type: sortType(arg.type), - }), - ); + return sortObjMap(args, arg => ({ + ...arg, + type: sortType(arg.type), + })); } function sortFields(fieldsMap) { return sortObjMap(fieldsMap, field => ({ + ...field, type: sortType(field.type), args: sortArgs(field.args), - resolve: field.resolve, - subscribe: field.subscribe, - deprecationReason: field.deprecationReason, - description: field.description, - astNode: field.astNode, })); } function sortInputFields(fieldsMap) { return sortObjMap(fieldsMap, field => ({ + ...field, type: sortType(field.type), - defaultValue: field.defaultValue, - description: field.description, - astNode: field.astNode, })); } @@ -121,54 +109,35 @@ export function lexicographicSortSchema(schema: GraphQLSchema): GraphQLSchema { if (isScalarType(type)) { return type; } else if (isObjectType(type)) { + const config = type.toConfig(); return new GraphQLObjectType({ - name: type.name, - interfaces: () => sortTypes(type.getInterfaces()), - fields: () => sortFields(type.getFields()), - isTypeOf: type.isTypeOf, - description: type.description, - astNode: type.astNode, - extensionASTNodes: type.extensionASTNodes, + ...config, + interfaces: () => sortTypes(config.interfaces), + fields: () => sortFields(config.fields), }); } else if (isInterfaceType(type)) { + const config = type.toConfig(); return new GraphQLInterfaceType({ - name: type.name, - fields: () => sortFields(type.getFields()), - resolveType: type.resolveType, - description: type.description, - astNode: type.astNode, - extensionASTNodes: type.extensionASTNodes, + ...config, + fields: () => sortFields(config.fields), }); } else if (isUnionType(type)) { + const config = type.toConfig(); return new GraphQLUnionType({ - name: type.name, - types: () => sortTypes(type.getTypes()), - resolveType: type.resolveType, - description: type.description, - astNode: type.astNode, + ...config, + types: () => sortTypes(config.types), }); } else if (isEnumType(type)) { + const config = type.toConfig(); return new GraphQLEnumType({ - name: type.name, - values: keyValMap( - sortByName(type.getValues()), - val => val.name, - val => ({ - value: val.value, - deprecationReason: val.deprecationReason, - description: val.description, - astNode: val.astNode, - }), - ), - description: type.description, - astNode: type.astNode, + ...config, + values: sortObjMap(config.values), }); } else if (isInputObjectType(type)) { + const config = type.toConfig(); return new GraphQLInputObjectType({ - name: type.name, - fields: () => sortInputFields(type.getFields()), - description: type.description, - astNode: type.astNode, + ...config, + fields: () => sortInputFields(config.fields), }); } throw new Error(`Unknown type: "${type}"`);