diff --git a/packages/gatsby/package.json b/packages/gatsby/package.json index 9514a9b0f65be..5b9394091eb2c 100644 --- a/packages/gatsby/package.json +++ b/packages/gatsby/package.json @@ -71,7 +71,7 @@ "gatsby-telemetry": "^1.0.4", "glob": "^7.1.1", "graphql": "^14.1.1", - "graphql-compose": "^6.1.1", + "graphql-compose": "^6.1.4", "graphql-playground-middleware-express": "^1.7.10", "graphql-relay": "^0.6.0", "graphql-tools": "^3.0.4", diff --git a/packages/gatsby/src/schema/infer/index.js b/packages/gatsby/src/schema/infer/index.js index 9e03a34afd7f7..120837a4bd069 100644 --- a/packages/gatsby/src/schema/infer/index.js +++ b/packages/gatsby/src/schema/infer/index.js @@ -58,8 +58,19 @@ const addInferredTypes = ({ report.panic(`Building schema failed`) } - return nodeTypesToInfer.map(typeName => + nodeTypesToInfer.forEach(typeName => { schemaComposer.getOrCreateOTC(typeName) + }) + + return nodeTypesToInfer.map(typeName => + addInferredType({ + schemaComposer, + typeName, + nodeStore, + typeConflictReporter, + typeMapping, + parentSpan, + }) ) } diff --git a/packages/gatsby/src/schema/schema.js b/packages/gatsby/src/schema/schema.js index 010303cdad74f..143e45667f19a 100644 --- a/packages/gatsby/src/schema/schema.js +++ b/packages/gatsby/src/schema/schema.js @@ -7,6 +7,9 @@ const { assertValidName, getNamedType, Kind, + GraphQLObjectType, + GraphQLInterfaceType, + GraphQLUnionType, } = require(`graphql`) const { ObjectTypeComposer, @@ -15,6 +18,10 @@ const { InputTypeComposer, GraphQLJSON, } = require(`graphql-compose`) +const { + defineFieldMapToConfig, +} = require(`graphql-compose/lib/utils/configToDefine`) + const apiRunner = require(`../utils/api-runner-node`) const report = require(`gatsby-cli/lib/reporter`) const { addNodeInterfaceFields } = require(`./types/node-interface`) @@ -89,24 +96,13 @@ const updateSchemaComposer = async ({ parentSpan, }) => { await addTypes({ schemaComposer, parentSpan, types }) - const inferredTypes = await addInferredTypes({ + await addInferredTypes({ schemaComposer, nodeStore, typeConflictReporter, typeMapping, parentSpan, }) - await processFieldExtensions({ schemaComposer }) - inferredTypes.map(inferredType => - addInferredType({ - schemaComposer, - typeName: inferredType.getTypeName(), - nodeStore, - typeConflictReporter, - typeMapping, - parentSpan, - }) - ) await addSetFieldsOnGraphQLNodeTypeFields({ schemaComposer, nodeStore, @@ -221,8 +217,9 @@ const processAddedType = ({ typeComposer.setExtension(`plugin`, plugin ? plugin.name : null) if (createdFrom === `sdl`) { - if (type.astNode && type.astNode.directives) { - type.astNode.directives.forEach(directive => { + const ast = typeComposer.getType().astNode + if (ast && ast.directives) { + ast.directives.forEach(directive => { if (directive.name.value === `infer`) { typeComposer.setExtension(`infer`, true) const addDefaultResolvers = getNoDefaultResolvers(directive) @@ -246,6 +243,37 @@ const processAddedType = ({ } } + if ( + typeComposer instanceof ObjectTypeComposer || + typeComposer instanceof InterfaceTypeComposer + ) { + typeComposer.getFieldNames().forEach(fieldName => { + typeComposer.setFieldExtension(fieldName, `createdFrom`, createdFrom) + typeComposer.setFieldExtension( + fieldName, + `plugin`, + plugin ? plugin.name : null + ) + + if (createdFrom === `sdl`) { + const field = typeComposer.getField(fieldName) + if (field.astNode && field.astNode.directives) { + field.astNode.directives.forEach(directive => { + if (directive.name.value === `addResolver`) { + const options = {} + directive.arguments.forEach(argument => { + options[argument.name.value] = GraphQLJSON.parseLiteral( + argument.value + ) + }) + typeComposer.setFieldExtension(fieldName, `addResolver`, options) + } + }) + } + } + }) + } + if (typeComposer.hasExtension(`addDefaultResolvers`)) { report.warn( `Deprecation warning - "noDefaultResolvers" is deprecated. In Gatsby 3, defined fields won't get resolvers, unless "addResolver" directive/extension is used.` @@ -268,33 +296,6 @@ const getNoDefaultResolvers = directive => { return null } -const processFieldExtensions = ({ schemaComposer }) => { - schemaComposer.forEach(typeComposer => { - if ( - typeComposer.getExtension(`createdFrom`) === `sdl` && - (typeComposer instanceof ObjectTypeComposer || - typeComposer instanceof InterfaceTypeComposer) - ) { - typeComposer.getFieldNames().forEach(fieldName => { - const field = typeComposer.getField(fieldName) - if (field.astNode && field.astNode.directives) { - field.astNode.directives.forEach(directive => { - if (directive.name.value === `addResolver`) { - const options = {} - directive.arguments.forEach(argument => { - options[argument.name.value] = GraphQLJSON.parseLiteral( - argument.value - ) - }) - typeComposer.setFieldExtension(fieldName, `addResolver`, options) - } - }) - } - }) - } - }) -} - const checkIsAllowedTypeName = name => { invariant( name !== `Node`, @@ -405,14 +406,6 @@ const addThirdPartySchemas = ({ }) => { thirdPartySchemas.forEach(schema => { const schemaQueryType = schema.getQueryType() - const queryTC = ObjectTypeComposer.createTemp(schemaQueryType) - processThirdPartyType({ - schemaComposer, - typeComposer: queryTC, - schemaQueryType, - }) - const fields = queryTC.getFields() - schemaComposer.Query.addFields(fields) // Explicitly add the third-party schema's types, so they can be targeted // in `createResolvers` API. @@ -424,38 +417,86 @@ const addThirdPartySchemas = ({ !isSpecifiedScalarType(type) && !isIntrospectionType(type) ) { - schemaComposer.addAsComposer(type) - const typeComposer = schemaComposer.getAnyTC(type.name) - processThirdPartyType({ schemaComposer, typeComposer, schemaQueryType }) - schemaComposer.addSchemaMustHaveType(typeComposer) + processThirdPartyType({ schemaComposer, type, schemaQueryType }) } }) + + const queryTC = ObjectTypeComposer.createTemp( + { + name: `TempQuery`, + fields: processThirdPartyTypeFields({ + type: schemaQueryType, + schemaQueryType, + }), + }, + schemaComposer + ) + schemaComposer.Query.addFields(queryTC.getFields()) }) } -const processThirdPartyType = ({ - schemaComposer, - typeComposer, - schemaQueryType, -}) => { - typeComposer.setExtension(`createdFrom`, `thirdPartySchema`) +const processThirdPartyType = ({ schemaComposer, type, schemaQueryType }) => { + let typeComposer // Fix for types that refer to Query. Thanks Relay Classic! if ( - typeComposer instanceof ObjectTypeComposer || - typeComposer instanceof InterfaceTypeComposer + type instanceof GraphQLObjectType || + type instanceof GraphQLInterfaceType ) { - typeComposer.getFieldNames().forEach(fieldName => { - const fieldType = typeComposer.getFieldType(fieldName) - if (getNamedType(fieldType) === schemaQueryType) { - typeComposer.extendField(fieldName, { - type: fieldType.toString().replace(schemaQueryType.name, `Query`), - }) - } - }) + const fields = processThirdPartyTypeFields({ type, schemaQueryType }) + if (type instanceof GraphQLObjectType) { + typeComposer = ObjectTypeComposer.create( + { + name: type.name, + fields, + interfaces: () => + type + .getInterfaces() + .map(iface => schemaComposer.getIFTC(iface.toString()).getType()), + }, + schemaComposer + ) + } else { + typeComposer = schemaComposer.getOrCreateIFTC(type.name) + typeComposer.setResolveType(type.resolveType) + } + typeComposer.setFields(fields) + } else if (type instanceof GraphQLUnionType) { + const types = type.getTypes() + typeComposer = schemaComposer.getOrCreateUTC(type.name) + typeComposer.setResolveType(type.resolveType) + typeComposer.setTypes(types.map(type => type.toString())) + schemaComposer.add(typeComposer) + } else { + schemaComposer.addAsComposer(type) + typeComposer = schemaComposer.get(type.name) } + typeComposer.setExtension(`createdFrom`, `thirdPartySchema`) + schemaComposer.addSchemaMustHaveType(typeComposer) + return typeComposer } +const processThirdPartyTypeFields = ({ type, schemaQueryType }) => { + const fields = {} + const typeFields = defineFieldMapToConfig(type.getFields()) + Object.keys(typeFields).forEach(fieldName => { + const field = typeFields[fieldName] + const fieldType = field.type + if (getNamedType(fieldType) === schemaQueryType) { + fields[fieldName] = { + ...field, + type: fieldType.toString().replace(schemaQueryType.name, `Query`), + } + } else { + fields[fieldName] = { + ...field, + type: fieldType.toString(), + } + } + }) + return fields +} + const addCustomResolveFunctions = async ({ schemaComposer, parentSpan }) => { const intermediateSchema = schemaComposer.buildSchema() const createResolvers = resolvers => { diff --git a/yarn.lock b/yarn.lock index 3a9ed5bc5c65e..fcee12ac0cefc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9002,10 +9002,10 @@ graceful-fs@^4.1.15, graceful-fs@^4.1.9: version "1.0.1" resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" -graphql-compose@^6.1.1: - version "6.1.1" - resolved "https://registry.yarnpkg.com/graphql-compose/-/graphql-compose-6.1.1.tgz#3764de5b2c48fbbd83d7b43809b76deac53734c2" - integrity sha512-54ZRGrbuCsCZw9NCe4k1IScVV0p1c7lkDXXBca0e1KVo80e8LT5+T4fUHaG6R0uXEcMlSijKZmRrbcj+eITNkg== +graphql-compose@^6.1.4: + version "6.1.4" + resolved "https://registry.yarnpkg.com/graphql-compose/-/graphql-compose-6.1.4.tgz#3c9b9688c622a5c82fdf65744f9af93e0eb4dc00" + integrity sha512-xMfLkUMRRIbVvsp8KhiB6VR7ZYiaerzubL07yWmqWa8GB64kvlkRHMBZfuDeka+U5NncF+if5fcJJusvsc1zMg== dependencies: graphql-type-json "^0.2.2" object-path "^0.11.4"