From a4109a839e945d275e2e3c0cbeb3fcd6aff54706 Mon Sep 17 00:00:00 2001 From: Kamil Kisiela Date: Wed, 1 Apr 2020 15:01:21 +0200 Subject: [PATCH] No polyfill --- packages/core/package.json | 1 - packages/polyfill/.gitignore | 72 ---- packages/polyfill/.npmignore | 13 - packages/polyfill/package.json | 36 -- packages/polyfill/src/index.ts | 1 - packages/polyfill/src/to-config.ts | 385 ------------------ packages/polyfill/tsconfig.json | 24 -- packages/schema/package.json | 1 - .../schema/src/add-resolvers-to-schema.ts | 33 +- packages/schema/src/heal.ts | 329 +++++---------- 10 files changed, 123 insertions(+), 772 deletions(-) delete mode 100644 packages/polyfill/.gitignore delete mode 100644 packages/polyfill/.npmignore delete mode 100644 packages/polyfill/package.json delete mode 100644 packages/polyfill/src/index.ts delete mode 100644 packages/polyfill/src/to-config.ts delete mode 100644 packages/polyfill/tsconfig.json diff --git a/packages/core/package.json b/packages/core/package.json index aa7078d5..db8ebd58 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -35,7 +35,6 @@ }, "dependencies": { "@graphql-toolkit/common": "0.9.12", - "@graphql-toolkit/polyfill": "0.9.12", "@graphql-toolkit/types": "0.9.12", "@graphql-toolkit/version": "0.9.12", "@graphql-toolkit/schema-merging": "0.9.12", diff --git a/packages/polyfill/.gitignore b/packages/polyfill/.gitignore deleted file mode 100644 index b2931f95..00000000 --- a/packages/polyfill/.gitignore +++ /dev/null @@ -1,72 +0,0 @@ -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* - -# Runtime data -pids -*.pid -*.seed -*.pid.lock - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage - -# nyc test coverage -.nyc_output - -# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# Bower dependency directory (https://bower.io/) -bower_components - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (https://nodejs.org/api/addons.html) -build/Release - -# Dependency directories -node_modules/ -jspm_packages/ - -# TypeScript v1 declaration files -typings/ - -# Optional npm cache directory -.npm - -# Optional eslint cache -.eslintcache - -# Optional REPL history -.node_repl_history - -# Output of 'npm pack' -*.tgz - -# Yarn Integrity file -.yarn-integrity - -# dotenv environment variables file -.env - -# next.js build output -.next - - -dist -build -temp -.idea - -test-results/ -junit.xml - -*.tgz \ No newline at end of file diff --git a/packages/polyfill/.npmignore b/packages/polyfill/.npmignore deleted file mode 100644 index 4ff88439..00000000 --- a/packages/polyfill/.npmignore +++ /dev/null @@ -1,13 +0,0 @@ -src -node_modules -tests -!dist -.circleci -.prettierrc -bump.js -jest.config.js -tsconfig.json -yarn.lock -yarn-error.log -bundle-test -*.tgz \ No newline at end of file diff --git a/packages/polyfill/package.json b/packages/polyfill/package.json deleted file mode 100644 index c13b1572..00000000 --- a/packages/polyfill/package.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "name": "@graphql-toolkit/polyfill", - "version": "0.9.12", - "description": "Common types for GraphQL Toolkit", - "repository": "git@github.com:ardatan/graphql-toolkit.git", - "author": "Kamil Kisiela ", - "license": "MIT", - "scripts": { - "lint": "tslint src/**/*.ts", - "clean": "rimraf dist", - "prebuild": "yarn clean", - "build": "bob", - "prepack": "bob-update-version", - "test": "jest --passWithNoTests --no-watchman --config ../../jest.config.js" - }, - "sideEffects": false, - "main": "dist/index.cjs.js", - "module": "dist/index.esm.js", - "typings": "dist/index.d.ts", - "typescript": { - "definition": "dist/index.d.ts" - }, - "peerDependencies": { - "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0" - }, - "buildOptions": { - "input": "./src/index.ts" - }, - "dependencies": { - "@graphql-toolkit/version": "0.9.12" - }, - "publishConfig": { - "access": "public", - "directory": "dist" - } -} diff --git a/packages/polyfill/src/index.ts b/packages/polyfill/src/index.ts deleted file mode 100644 index 7ca7238b..00000000 --- a/packages/polyfill/src/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { toConfig } from './to-config'; diff --git a/packages/polyfill/src/to-config.ts b/packages/polyfill/src/to-config.ts deleted file mode 100644 index 2f99d020..00000000 --- a/packages/polyfill/src/to-config.ts +++ /dev/null @@ -1,385 +0,0 @@ -// graphql = []; - - const types = schema.getTypeMap(); - Object.keys(types).forEach((typeName) => { - newTypes.push(types[typeName]); - }); - - const schemaConfig = { - query: schema.getQueryType(), - mutation: schema.getMutationType(), - subscription: schema.getSubscriptionType(), - types: newTypes, - directives: schema.getDirectives().slice(), - extensions: schema.extensions, - astNode: schema.astNode, - extensionASTNodes: schema.extensionASTNodes != null ? schema.extensionASTNodes : [], - assumeValid: (schema as { __validationErrors?: boolean }).__validationErrors !== undefined, - }; - - if (version >= 15) { - (schemaConfig as { - description?: string; - }).description = (schema as { - description?: string; - }).description; - } - - return schemaConfig; -} - -export function toConfig(graphqlObject: GraphQLSchema): GraphQLSchemaConfig; -export function toConfig( - graphqlObject: GraphQLObjectTypeConfig & { - interfaces: Array; - fields: GraphQLFieldConfigMap; - } -): GraphQLObjectTypeConfig; -export function toConfig( - graphqlObject: GraphQLInterfaceType -): GraphQLInterfaceTypeConfig & { - fields: GraphQLFieldConfigMap; -}; -export function toConfig( - graphqlObject: GraphQLUnionType -): GraphQLUnionTypeConfig & { - types: Array; -}; -export function toConfig(graphqlObject: GraphQLEnumType): GraphQLEnumTypeConfig; -export function toConfig(graphqlObject: GraphQLScalarType): GraphQLScalarTypeConfig; -export function toConfig( - graphqlObject: GraphQLInputObjectType -): GraphQLInputObjectTypeConfig & { - fields: GraphQLInputFieldConfigMap; -}; -export function toConfig(graphqlObject: GraphQLDirective): GraphQLDirectiveConfig; -export function toConfig(graphqlObject: GraphQLInputField): GraphQLInputFieldConfig; -export function toConfig(graphqlObject: GraphQLField): GraphQLFieldConfig; -export function toConfig(graphqlObject: any): any; -export function toConfig(graphqlObject: any) { - if (isSchema(graphqlObject)) { - return schemaToConfig(graphqlObject); - } else if (isDirective(graphqlObject)) { - return directiveToConfig(graphqlObject); - } else if (isNamedType(graphqlObject)) { - return typeToConfig(graphqlObject); - } - - // Input and output fields do not have predicates defined, but using duck typing, - // type is defined for input and output fields - if (graphqlObject.type != null) { - if (graphqlObject.args != null || graphqlObject.resolve != null || graphqlObject.subscribe != null) { - return fieldToConfig(graphqlObject); - } else if (graphqlObject.defaultValue !== undefined) { - return inputFieldToConfig(graphqlObject); - } - - // Not all input and output fields can be checked by above in older versions - // of graphql, but almost all properties on the field and config are identical. - // In particular, just name and isDeprecated should be removed. - const { name, isDeprecated, ...rest } = graphqlObject; - return { - ...rest, - }; - } - - throw new Error(`Unknown graphql object ${graphqlObject as string}`); -} - -export function typeToConfig(type: GraphQLObjectType): GraphQLObjectTypeConfig; -export function typeToConfig(type: GraphQLInterfaceType): GraphQLInterfaceTypeConfig; -export function typeToConfig(type: GraphQLUnionType): GraphQLUnionTypeConfig; -export function typeToConfig(type: GraphQLEnumType): GraphQLEnumTypeConfig; -export function typeToConfig(type: GraphQLScalarType): GraphQLScalarTypeConfig; -export function typeToConfig(type: GraphQLInputObjectType): GraphQLInputObjectTypeConfig; -export function typeToConfig(type: any): any; -export function typeToConfig(type: any) { - if (isObjectType(type)) { - return objectTypeToConfig(type); - } else if (isInterfaceType(type)) { - return interfaceTypeToConfig(type); - } else if (isUnionType(type)) { - return unionTypeToConfig(type); - } else if (isEnumType(type)) { - return enumTypeToConfig(type); - } else if (isScalarType(type)) { - return scalarTypeToConfig(type); - } else if (isInputObjectType(type)) { - return inputObjectTypeToConfig(type); - } - - throw new Error(`Unknown type ${type as string}`); -} - -export function objectTypeToConfig(type: GraphQLObjectType): GraphQLObjectTypeConfig { - if (type.toConfig != null) { - return type.toConfig(); - } - - const typeConfig = { - name: type.name, - description: type.description, - interfaces: type.getInterfaces(), - fields: fieldMapToConfig(type.getFields()), - isTypeOf: type.isTypeOf, - extensions: type.extensions, - astNode: type.astNode, - extensionASTNodes: type.extensionASTNodes != null ? type.extensionASTNodes : [], - }; - - return typeConfig; -} - -export function interfaceTypeToConfig(type: GraphQLInterfaceType): GraphQLInterfaceTypeConfig { - if (type.toConfig != null) { - return type.toConfig(); - } - - const typeConfig = { - name: type.name, - description: type.description, - fields: fieldMapToConfig(type.getFields()), - resolveType: type.resolveType, - extensions: type.extensions, - astNode: type.astNode, - extensionASTNodes: type.extensionASTNodes != null ? type.extensionASTNodes : [], - }; - - if (version >= 15) { - ((typeConfig as unknown) as GraphQLObjectTypeConfig< - any, - any - >).interfaces = ((type as unknown) as GraphQLObjectType).getInterfaces(); - } - - return typeConfig; -} - -export function unionTypeToConfig(type: GraphQLUnionType): GraphQLUnionTypeConfig { - if (type.toConfig != null) { - return type.toConfig(); - } - - const typeConfig = { - name: type.name, - description: type.description, - types: type.getTypes(), - resolveType: type.resolveType, - extensions: type.extensions, - astNode: type.astNode, - extensionASTNodes: type.extensionASTNodes != null ? type.extensionASTNodes : [], - }; - - return typeConfig; -} - -export function enumTypeToConfig(type: GraphQLEnumType): GraphQLEnumTypeConfig { - if (type.toConfig != null) { - return type.toConfig(); - } - - const newValues = {}; - - type.getValues().forEach((value) => { - newValues[value.name] = { - description: value.description, - value: value.value, - deprecationReason: value.deprecationReason, - extensions: value.extensions, - astNode: value.astNode, - }; - }); - - const typeConfig = { - name: type.name, - description: type.description, - values: newValues, - extensions: type.extensions, - astNode: type.astNode, - extensionASTNodes: type.extensionASTNodes != null ? type.extensionASTNodes : [], - }; - - return typeConfig; -} - -const hasOwn = Object.prototype.hasOwnProperty; - -export function scalarTypeToConfig(type: GraphQLScalarType): GraphQLScalarTypeConfig { - if (type.toConfig != null) { - return type.toConfig(); - } - - const typeConfig = { - name: type.name, - description: type.description, - serialize: - version >= 14 || hasOwn.call(type, 'serialize') - ? type.serialize - : ((type as unknown) as { - _scalarConfig: GraphQLScalarTypeConfig; - })._scalarConfig.serialize, - parseValue: - version >= 14 || hasOwn.call(type, 'parseValue') - ? type.parseValue - : ((type as unknown) as { - _scalarConfig: GraphQLScalarTypeConfig; - })._scalarConfig.parseValue, - parseLiteral: - version >= 14 || hasOwn.call(type, 'parseLiteral') - ? type.parseLiteral - : ((type as unknown) as { - _scalarConfig: GraphQLScalarTypeConfig; - })._scalarConfig.parseLiteral, - extensions: type.extensions, - astNode: type.astNode, - extensionASTNodes: type.extensionASTNodes != null ? type.extensionASTNodes : [], - }; - - return typeConfig; -} - -export function inputObjectTypeToConfig(type: GraphQLInputObjectType): GraphQLInputObjectTypeConfig { - if (type.toConfig != null) { - return type.toConfig(); - } - - const typeConfig = { - name: type.name, - description: type.description, - fields: inputFieldMapToConfig(type.getFields()), - extensions: type.extensions, - astNode: type.astNode, - extensionASTNodes: type.extensionASTNodes != null ? type.extensionASTNodes : [], - }; - - return typeConfig; -} - -export function inputFieldMapToConfig(fields: GraphQLInputFieldMap): GraphQLInputFieldConfigMap { - const newFields = {}; - Object.keys(fields).forEach((fieldName) => { - const field = fields[fieldName]; - newFields[fieldName] = toConfig(field); - }); - - return newFields; -} - -export function inputFieldToConfig(field: GraphQLInputField): GraphQLInputFieldConfig { - return { - description: field.description, - type: field.type, - defaultValue: field.defaultValue, - extensions: field.extensions, - astNode: field.astNode, - }; -} - -export function directiveToConfig(directive: GraphQLDirective): GraphQLDirectiveConfig { - if (directive.toConfig != null) { - return directive.toConfig(); - } - - const directiveConfig = { - name: directive.name, - description: directive.description, - locations: directive.locations, - args: argumentMapToConfig(directive.args), - isRepeatable: ((directive as unknown) as { isRepeatable: boolean }).isRepeatable, - extensions: directive.extensions, - astNode: directive.astNode, - }; - - return directiveConfig; -} - -export function fieldMapToConfig(fields: GraphQLFieldMap): GraphQLFieldConfigMap { - const newFields = {}; - - Object.keys(fields).forEach((fieldName) => { - const field = fields[fieldName]; - newFields[fieldName] = toConfig(field); - }); - - return newFields; -} - -export function fieldToConfig(field: GraphQLField): GraphQLFieldConfig { - return { - description: field.description, - type: field.type, - args: argumentMapToConfig(field.args), - resolve: field.resolve, - subscribe: field.subscribe, - deprecationReason: field.deprecationReason, - extensions: field.extensions, - astNode: field.astNode, - }; -} - -export function argumentMapToConfig(args: ReadonlyArray): GraphQLFieldConfigArgumentMap { - const newArguments = {}; - args.forEach((arg) => { - newArguments[arg.name] = argumentToConfig(arg); - }); - - return newArguments; -} - -export function argumentToConfig(arg: GraphQLArgument): GraphQLArgumentConfig { - return { - description: arg.description, - type: arg.type, - defaultValue: arg.defaultValue, - extensions: arg.extensions, - astNode: arg.astNode, - }; -} diff --git a/packages/polyfill/tsconfig.json b/packages/polyfill/tsconfig.json deleted file mode 100644 index 6ae81d4b..00000000 --- a/packages/polyfill/tsconfig.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "compilerOptions": { - "importHelpers": true, - "experimentalDecorators": true, - "module": "esnext", - "target": "es2018", - "lib": ["esnext"], - "suppressImplicitAnyIndexErrors": true, - "moduleResolution": "node", - "emitDecoratorMetadata": true, - "sourceMap": true, - "declaration": true, - "outDir": "./dist/", - "noImplicitAny": false, - "noImplicitThis": true, - "alwaysStrict": true, - "noImplicitReturns": true, - "noUnusedLocals": false, - "esModuleInterop": true, - "allowSyntheticDefaultImports": true, - "noUnusedParameters": false - }, - "include": ["src"] -} diff --git a/packages/schema/package.json b/packages/schema/package.json index a0bfc85a..70eabff4 100644 --- a/packages/schema/package.json +++ b/packages/schema/package.json @@ -33,7 +33,6 @@ "@graphql-toolkit/common": "0.9.12", "@graphql-toolkit/types": "0.9.12", "@graphql-toolkit/version": "0.9.12", - "@graphql-toolkit/polyfill": "0.9.12", "tslib": "1.11.1" }, "publishConfig": { diff --git a/packages/schema/src/add-resolvers-to-schema.ts b/packages/schema/src/add-resolvers-to-schema.ts index ce580c1d..59747dca 100644 --- a/packages/schema/src/add-resolvers-to-schema.ts +++ b/packages/schema/src/add-resolvers-to-schema.ts @@ -8,9 +8,9 @@ import { isUnionType, isInterfaceType, isObjectType, + GraphQLEnumTypeConfig, } from 'graphql'; import { IResolvers } from '@graphql-toolkit/types'; -import { toConfig } from '@graphql-toolkit/polyfill'; import { SchemaError, forEachField, @@ -97,7 +97,7 @@ export function addResolversToSchema( } }); - const config = toConfig(type); + const config = enumTypeToConfig(type); const values = type.getValues(); const newValues = {}; @@ -187,3 +187,32 @@ function setFieldProperties(field: GraphQLField, propertiesObj: Record field[propertyName] = propertiesObj[propertyName]; }); } + +function enumTypeToConfig(type: GraphQLEnumType): GraphQLEnumTypeConfig { + if (type.toConfig != null) { + return type.toConfig(); + } + + const newValues = {}; + + type.getValues().forEach((value) => { + newValues[value.name] = { + description: value.description, + value: value.value, + deprecationReason: value.deprecationReason, + extensions: value.extensions, + astNode: value.astNode, + }; + }); + + const typeConfig = { + name: type.name, + description: type.description, + values: newValues, + extensions: type.extensions, + astNode: type.astNode, + extensionASTNodes: type.extensionASTNodes != null ? type.extensionASTNodes : [], + }; + + return typeConfig; +} diff --git a/packages/schema/src/heal.ts b/packages/schema/src/heal.ts index edd4009a..ce5cc0be 100644 --- a/packages/schema/src/heal.ts +++ b/packages/schema/src/heal.ts @@ -1,28 +1,20 @@ import { + GraphQLSchema, + GraphQLNamedType, GraphQLDirective, - GraphQLInputObjectType, + GraphQLObjectType, GraphQLInterfaceType, + GraphQLInputObjectType, + GraphQLScalarType, + GraphQLUnionType, + GraphQLEnumType, + GraphQLType, GraphQLList, - GraphQLObjectType, - GraphQLNamedType, GraphQLNonNull, - GraphQLType, - GraphQLUnionType, isNamedType, - GraphQLSchema, - GraphQLInputType, - GraphQLOutputType, - isObjectType, - isInterfaceType, - isUnionType, - isInputObjectType, - isLeafType, - isListType, - isNonNullType, } from 'graphql'; -import { version } from '@graphql-toolkit/version'; -import { toConfig } from '@graphql-toolkit/polyfill'; -import { each, updateEachKey, isStub, getBuiltInForStub } from '@graphql-toolkit/common'; +import { VisitableSchemaType } from './types'; +import { each, updateEachKey } from '@graphql-toolkit/common'; type NamedTypeMap = { [key: string]: GraphQLNamedType; @@ -30,253 +22,116 @@ type NamedTypeMap = { const hasOwn = Object.prototype.hasOwnProperty; -// Update any references to named schema types that disagree with the named -// types found in schema.getTypeMap(). -export function healSchema(schema: GraphQLSchema): GraphQLSchema { - const typeMap = schema.getTypeMap(); - const directives = schema.getDirectives(); - - const queryType = schema.getQueryType(); - const mutationType = schema.getMutationType(); - const subscriptionType = schema.getSubscriptionType(); - - const newQueryTypeName = - queryType != null ? (typeMap[queryType.name] != null ? typeMap[queryType.name].name : undefined) : undefined; - const newMutationTypeName = - mutationType != null - ? typeMap[mutationType.name] != null - ? typeMap[mutationType.name].name - : undefined - : undefined; - const newSubscriptionTypeName = - subscriptionType != null - ? typeMap[subscriptionType.name] != null - ? typeMap[subscriptionType.name].name - : undefined - : undefined; - - healTypes(typeMap, directives); - - const filteredTypeMap = {}; - - Object.keys(typeMap).forEach((typeName) => { - if (!typeName.startsWith('__')) { - filteredTypeMap[typeName] = typeMap[typeName]; - } - }); - - const healedSchema = new GraphQLSchema({ - ...toConfig(schema), - query: newQueryTypeName ? filteredTypeMap[newQueryTypeName] : undefined, - mutation: newMutationTypeName ? filteredTypeMap[newMutationTypeName] : undefined, - subscription: newSubscriptionTypeName ? filteredTypeMap[newSubscriptionTypeName] : undefined, - types: Object.keys(filteredTypeMap).map((typeName) => filteredTypeMap[typeName]), - directives: directives.slice(), - }); - - // Reconstruct the schema to reinitialize private variables - // e.g. the stored implementation map and the proper root types. - Object.assign(schema, healedSchema); - +export function healSchema(schema: GraphQLSchema) { + heal(schema); return schema; -} -export function healTypes( - originalTypeMap: Record, - directives: ReadonlyArray, - config: { - skipPruning: boolean; - } = { - skipPruning: false, - } -) { - const actualNamedTypeMap: NamedTypeMap = Object.create(null); + function heal(type: VisitableSchemaType) { + if (type instanceof GraphQLSchema) { + const originalTypeMap: NamedTypeMap = type.getTypeMap(); + const actualNamedTypeMap: NamedTypeMap = Object.create(null); - // If any of the .name properties of the GraphQLNamedType objects in - // schema.getTypeMap() have changed, the keys of the type map need to - // be updated accordingly. - - each(originalTypeMap, (namedType, typeName) => { - if (namedType == null || typeName.startsWith('__')) { - return; - } - - const actualName = namedType.name; - if (actualName.startsWith('__')) { - return; - } + // If any of the .name properties of the GraphQLNamedType objects in + // schema.getTypeMap() have changed, the keys of the type map need to + // be updated accordingly. - if (hasOwn.call(actualNamedTypeMap, actualName)) { - throw new Error(`Duplicate schema type name ${actualName}`); - } + each(originalTypeMap, (namedType, typeName) => { + if (typeName.startsWith('__')) { + return; + } - actualNamedTypeMap[actualName] = namedType; + const actualName = namedType.name; + if (actualName.startsWith('__')) { + return; + } - // Note: we are deliberately leaving namedType in the schema by its - // original name (which might be different from actualName), so that - // references by that name can be healed. - }); + if (hasOwn.call(actualNamedTypeMap, actualName)) { + throw new Error(`Duplicate schema type name ${actualName}`); + } - // Now add back every named type by its actual name. - each(actualNamedTypeMap, (namedType, typeName) => { - originalTypeMap[typeName] = namedType; - }); + actualNamedTypeMap[actualName] = namedType; - // Directive declaration argument types can refer to named types. - each(directives, (decl: GraphQLDirective) => { - updateEachKey(decl.args, (arg) => { - arg.type = healType(arg.type) as GraphQLInputType; - return arg.type === null ? null : arg; - }); - }); + // Note: we are deliberately leaving namedType in the schema by its + // original name (which might be different from actualName), so that + // references by that name can be healed. + }); - each(originalTypeMap, (namedType, typeName) => { - // Heal all named types, except for dangling references, kept only to redirect. - if (!typeName.startsWith('__') && hasOwn.call(actualNamedTypeMap, typeName)) { - if (namedType != null) { - healNamedType(namedType); - } - } - }); + // Now add back every named type by its actual name. + each(actualNamedTypeMap, (namedType, typeName) => { + originalTypeMap[typeName] = namedType; + }); - updateEachKey(originalTypeMap, (_namedType, typeName) => { - // Dangling references to renamed types should remain in the schema - // during healing, but must be removed now, so that the following - // invariant holds for all names: schema.getType(name).name === name - if (!typeName.startsWith('__') && !hasOwn.call(actualNamedTypeMap, typeName)) { - return null; - } - }); + // Directive declaration argument types can refer to named types. + each(type.getDirectives(), (decl: GraphQLDirective) => { + if (decl.args) { + each(decl.args, (arg) => { + arg.type = healType(arg.type); + }); + } + }); - if (!config.skipPruning) { - pruneTypes(originalTypeMap, directives); - } + each(originalTypeMap, (namedType, typeName) => { + if (!typeName.startsWith('__')) { + heal(namedType); + } + }); - function healNamedType(type: GraphQLNamedType) { - if (isObjectType(type)) { + updateEachKey(originalTypeMap, (namedType, typeName) => { + // Dangling references to renamed types should remain in the schema + // during healing, but must be removed now, so that the following + // invariant holds for all names: schema.getType(name).name === name + if (!typeName.startsWith('__') && !hasOwn.call(actualNamedTypeMap, typeName)) { + return null; + } + }); + } else if (type instanceof GraphQLObjectType) { healFields(type); - healInterfaces(type); - return; - } else if (isInterfaceType(type)) { + each(type.getInterfaces(), (iface) => heal(iface)); + } else if (type instanceof GraphQLInterfaceType) { healFields(type); - if (version >= 15) { - healInterfaces(type); - } - return; - } else if (isUnionType(type)) { - healUnderlyingTypes(type); - return; - } else if (isInputObjectType(type)) { - healInputFields(type); - return; - } else if (isLeafType(type)) { - return; + } else if (type instanceof GraphQLInputObjectType) { + each(type.getFields(), (field) => { + field.type = healType(field.type); + }); + } else if (type instanceof GraphQLScalarType) { + // Nothing to do. + } else if (type instanceof GraphQLUnionType) { + updateEachKey(type.getTypes(), (t) => healType(t)); + } else if (type instanceof GraphQLEnumType) { + // Nothing to do. + } else { + throw new Error(`Unexpected schema type: ${type}`); } - - throw new Error(`Unexpected schema type: ${type as string}`); } function healFields(type: GraphQLObjectType | GraphQLInterfaceType) { - updateEachKey(type.getFields(), (field) => { - updateEachKey(field.args, (arg) => { - arg.type = healType(arg.type) as GraphQLInputType; - return arg.type === null ? null : arg; - }); - field.type = healType(field.type) as GraphQLOutputType; - return field.type === null ? null : field; - }); - } - - function healInterfaces(type: GraphQLObjectType | GraphQLInterfaceType) { - updateEachKey((type as GraphQLObjectType).getInterfaces(), (iface) => { - const healedType = healType(iface) as GraphQLInterfaceType; - return healedType; - }); - } - - function healInputFields(type: GraphQLInputObjectType) { - updateEachKey(type.getFields(), (field) => { - field.type = healType(field.type) as GraphQLInputType; - return field.type === null ? null : field; - }); - } - - function healUnderlyingTypes(type: GraphQLUnionType) { - updateEachKey(type.getTypes(), (t: GraphQLOutputType) => { - const healedType = healType(t) as GraphQLOutputType; - return healedType; + each(type.getFields(), (field) => { + field.type = healType(field.type); + if (field.args) { + each(field.args, (arg) => { + arg.type = healType(arg.type); + }); + } }); } - function healType(type: T): GraphQLType | null { + function healType(type: T): T { // Unwrap the two known wrapper types - if (isListType(type)) { - const healedType = healType(type.ofType); - return healedType != null ? new GraphQLList(healedType) : null; - } else if (isNonNullType(type)) { - const healedType = healType(type.ofType); - return healedType != null ? new GraphQLNonNull(healedType) : null; + if (type instanceof GraphQLList) { + type = new GraphQLList(healType(type.ofType)) as T; + } else if (type instanceof GraphQLNonNull) { + type = new GraphQLNonNull(healType(type.ofType)) as T; } else if (isNamedType(type)) { // If a type annotation on a field or an argument or a union member is // any `GraphQLNamedType` with a `name`, then it must end up identical // to `schema.getType(name)`, since `schema.getTypeMap()` is the source // of truth for all named schema types. - // Note that new types can still be simply added by adding a field, as - // the official type will be undefined, not null. - let officialType = originalTypeMap[type.name]; - if (officialType === undefined) { - if (isStub(type)) { - officialType = getBuiltInForStub(type); - } else { - officialType = type; - } - originalTypeMap[type.name] = officialType; - } - return officialType; - } - - return null; - } -} - -function pruneTypes(typeMap: Record, directives: ReadonlyArray) { - const implementedInterfaces = {}; - each(typeMap, (namedType) => { - if (isObjectType(namedType) || (version >= 15 && isInterfaceType(namedType))) { - each((namedType as GraphQLObjectType).getInterfaces(), (iface) => { - implementedInterfaces[iface.name] = true; - }); - } - }); - - let prunedTypeMap = false; - const typeNames = Object.keys(typeMap); - for (let i = 0; i < typeNames.length; i++) { - const typeName = typeNames[i]; - const type = typeMap[typeName]; - if (isObjectType(type) || isInputObjectType(type)) { - // prune types with no fields - if (!Object.keys(type.getFields()).length) { - typeMap[typeName] = null; - prunedTypeMap = true; - } - } else if (isUnionType(type)) { - // prune unions without underlying types - if (!type.getTypes().length) { - typeMap[typeName] = null; - prunedTypeMap = true; - } - } else if (isInterfaceType(type)) { - // prune interfaces without fields or without implementations - if (!Object.keys(type.getFields()).length || !implementedInterfaces[type.name]) { - typeMap[typeName] = null; - prunedTypeMap = true; + const namedType = type as GraphQLNamedType; + const officialType = schema.getType(namedType.name); + if (officialType && namedType !== officialType) { + return officialType as T; } } - } - - // every prune requires another round of healing - if (prunedTypeMap) { - healTypes(typeMap, directives); + return type; } }