"use strict"; const _ = require(`lodash`); const invariant = require(`invariant`); const { isSpecifiedScalarType, isIntrospectionType, assertValidName, parse, GraphQLNonNull, GraphQLList } = require(`graphql`); const { ObjectTypeComposer, InterfaceTypeComposer, UnionTypeComposer, InputTypeComposer, ScalarTypeComposer, EnumTypeComposer } = require(`graphql-compose`); const apiRunner = require(`../utils/api-runner-node`); const report = require(`gatsby-cli/lib/reporter`); const { addNodeInterfaceFields } = require(`./types/node-interface`); const { addInferredType, addInferredTypes } = require(`./infer`); const { findOne, findManyPaginated } = require(`./resolvers`); const { processFieldExtensions, internalExtensionNames } = require(`./extensions`); const { getPagination } = require(`./types/pagination`); const { getSortInput } = require(`./types/sort`); const { getFilterInput } = require(`./types/filter`); const { isGatsbyType, GatsbyGraphQLTypeKind } = require(`./types/type-builders`); const { printTypeDefinitions } = require(`./print`); const buildSchema = async ({ schemaComposer, nodeStore, types, typeMapping, fieldExtensions, thirdPartySchemas, printConfig, typeConflictReporter, parentSpan }) => { await updateSchemaComposer({ schemaComposer, nodeStore, types, typeMapping, fieldExtensions, thirdPartySchemas, printConfig, typeConflictReporter, parentSpan }); // const { printSchema } = require(`graphql`) const schema = schemaComposer.buildSchema(); // console.log(printSchema(schema)) return schema; }; const rebuildSchemaWithSitePage = async ({ schemaComposer, nodeStore, typeMapping, fieldExtensions, typeConflictReporter, parentSpan }) => { const typeComposer = schemaComposer.getOTC(`SitePage`); const shouldInfer = !typeComposer.hasExtension(`infer`) || typeComposer.getExtension(`infer`) !== false; if (shouldInfer) { addInferredType({ schemaComposer, typeComposer, nodeStore, typeConflictReporter, typeMapping, parentSpan }); } await processTypeComposer({ schemaComposer, typeComposer, fieldExtensions, nodeStore, parentSpan }); return schemaComposer.buildSchema(); }; module.exports = { buildSchema, rebuildSchemaWithSitePage }; const updateSchemaComposer = async ({ schemaComposer, nodeStore, types, typeMapping, fieldExtensions, thirdPartySchemas, printConfig, typeConflictReporter, parentSpan }) => { await addTypes({ schemaComposer, parentSpan, types }); await addInferredTypes({ schemaComposer, nodeStore, typeConflictReporter, typeMapping, parentSpan }); await printTypeDefinitions({ config: printConfig, schemaComposer }); await addSetFieldsOnGraphQLNodeTypeFields({ schemaComposer, nodeStore, parentSpan }); await Promise.all(Array.from(new Set(schemaComposer.values())).map(typeComposer => processTypeComposer({ schemaComposer, typeComposer, fieldExtensions, nodeStore, parentSpan }))); checkQueryableInterfaces({ schemaComposer }); await addConvenienceChildrenFields({ schemaComposer, parentSpan }); await addThirdPartySchemas({ schemaComposer, thirdPartySchemas, parentSpan }); await addCustomResolveFunctions({ schemaComposer, parentSpan }); }; const processTypeComposer = async ({ schemaComposer, typeComposer, fieldExtensions, nodeStore, parentSpan }) => { if (typeComposer instanceof ObjectTypeComposer) { await processFieldExtensions({ schemaComposer, typeComposer, fieldExtensions, parentSpan }); if (typeComposer.hasInterface(`Node`)) { await addNodeInterfaceFields({ schemaComposer, typeComposer, parentSpan }); await addImplicitConvenienceChildrenFields({ schemaComposer, typeComposer, nodeStore, parentSpan }); await addTypeToRootQuery({ schemaComposer, typeComposer, parentSpan }); } } else if (typeComposer instanceof InterfaceTypeComposer) { if (typeComposer.getExtension(`nodeInterface`)) { // We only process field extensions for queryable Node interfaces, so we get // the input args on the root query type, e.g. `formatString` etc. for `dateformat` await processFieldExtensions({ schemaComposer, typeComposer, fieldExtensions, parentSpan }); await addTypeToRootQuery({ schemaComposer, typeComposer, parentSpan }); } } }; const addTypes = ({ schemaComposer, types, parentSpan }) => { types.forEach(({ typeOrTypeDef, plugin }) => { if (typeof typeOrTypeDef === `string`) { let parsedTypes; const createdFrom = `sdl`; try { parsedTypes = parseTypeDefs({ typeDefs: typeOrTypeDef, plugin, createdFrom, schemaComposer, parentSpan }); } catch (error) { reportParsingError(error); return; } parsedTypes.forEach(type => { processAddedType({ schemaComposer, type, parentSpan, createdFrom, plugin }); }); } else if (isGatsbyType(typeOrTypeDef)) { const type = createTypeComposerFromGatsbyType({ schemaComposer, type: typeOrTypeDef, parentSpan }); if (type) { const typeName = type.getTypeName(); const createdFrom = `typeBuilder`; checkIsAllowedTypeName(typeName); if (schemaComposer.has(typeName)) { const typeComposer = schemaComposer.get(typeName); mergeTypes({ schemaComposer, typeComposer, type, plugin, createdFrom, parentSpan }); } else { processAddedType({ schemaComposer, type, parentSpan, createdFrom, plugin }); } } } else { const typeName = typeOrTypeDef.name; const createdFrom = `graphql-js`; checkIsAllowedTypeName(typeName); if (schemaComposer.has(typeName)) { const typeComposer = schemaComposer.get(typeName); mergeTypes({ schemaComposer, typeComposer, type: typeOrTypeDef, plugin, createdFrom, parentSpan }); } else { processAddedType({ schemaComposer, type: typeOrTypeDef, parentSpan, createdFrom, plugin }); } } }); }; const mergeTypes = ({ schemaComposer, typeComposer, type, plugin, createdFrom, parentSpan }) => { // Only allow user or plugin owning the type to extend already existing type. const typeOwner = typeComposer.getExtension(`plugin`); if (!plugin || plugin.name === `default-site-plugin` || plugin.name === typeOwner) { typeComposer.merge(type); if (isNamedTypeComposer(type)) { typeComposer.extendExtensions(type.getExtensions()); } addExtensions({ schemaComposer, typeComposer, plugin, createdFrom }); return true; } else { report.warn(`Plugin \`${plugin.name}\` tried to define the GraphQL type ` + `\`${typeComposer.getTypeName()}\`, which has already been defined ` + `by the plugin \`${typeOwner}\`.`); return false; } }; const processAddedType = ({ schemaComposer, type, parentSpan, createdFrom, plugin }) => { const typeName = schemaComposer.addAsComposer(type); const typeComposer = schemaComposer.get(typeName); if (typeComposer instanceof InterfaceTypeComposer || typeComposer instanceof UnionTypeComposer) { if (!typeComposer.getResolveType()) { typeComposer.setResolveType(node => node.internal.type); } } schemaComposer.addSchemaMustHaveType(typeComposer); addExtensions({ schemaComposer, typeComposer, plugin, createdFrom }); return typeComposer; }; const addExtensions = ({ schemaComposer, typeComposer, plugin, createdFrom }) => { typeComposer.setExtension(`createdFrom`, createdFrom); typeComposer.setExtension(`plugin`, plugin ? plugin.name : null); if (createdFrom === `sdl`) { const directives = typeComposer.getDirectives(); directives.forEach(({ name, args }) => { switch (name) { case `infer`: case `dontInfer`: typeComposer.setExtension(`infer`, name === `infer`); if (args.noDefaultResolvers != null) { typeComposer.setExtension(`addDefaultResolvers`, !args.noDefaultResolvers); } break; case `mimeTypes`: typeComposer.setExtension(`mimeTypes`, args); break; case `childOf`: typeComposer.setExtension(`childOf`, args); break; case `nodeInterface`: if (typeComposer instanceof InterfaceTypeComposer) { if (!typeComposer.hasField(`id`) || typeComposer.getFieldType(`id`).toString() !== `ID!`) { report.panic(`Interfaces with the \`nodeInterface\` extension must have a field ` + `\`id\` of type \`ID!\`. Check the type definition of ` + `\`${typeComposer.getTypeName()}\`.`); } typeComposer.setExtension(`nodeInterface`, true); } break; default: } }); } if (typeComposer instanceof ObjectTypeComposer || typeComposer instanceof InterfaceTypeComposer || typeComposer instanceof InputTypeComposer) { typeComposer.getFieldNames().forEach(fieldName => { typeComposer.setFieldExtension(fieldName, `createdFrom`, createdFrom); typeComposer.setFieldExtension(fieldName, `plugin`, plugin ? plugin.name : null); if (createdFrom === `sdl`) { const directives = typeComposer.getFieldDirectives(fieldName); directives.forEach(({ name, args }) => { typeComposer.setFieldExtension(fieldName, name, args); }); } // Validate field extension args. `graphql-compose` already checks the // type of directive args in `parseDirectives`, but we want to check // extensions provided with type builders as well. Also, we warn if an // extension option was provided which does not exist in the field // extension definition. const fieldExtensions = typeComposer.getFieldExtensions(fieldName); const typeName = typeComposer.getTypeName(); Object.keys(fieldExtensions).filter(name => !internalExtensionNames.includes(name)).forEach(name => { const args = fieldExtensions[name]; if (!args || typeof args !== `object`) { report.error(`Field extension arguments must be provided as an object. ` + `Received "${args}" on \`${typeName}.${fieldName}\`.`); return; } try { const definition = schemaComposer.getDirective(name); // Handle `defaultValue` when not provided as directive definition.args.forEach(({ name, defaultValue }) => { if (args[name] === undefined && defaultValue !== undefined) { args[name] = defaultValue; } }); Object.keys(args).forEach(arg => { const argumentDef = definition.args.find(({ name }) => name === arg); if (!argumentDef) { report.error(`Field extension \`${name}\` on \`${typeName}.${fieldName}\` ` + `has invalid argument \`${arg}\`.`); return; } const value = args[arg]; try { validate(argumentDef.type, value); } catch (error) { report.error(`Field extension \`${name}\` on \`${typeName}.${fieldName}\` ` + `has argument \`${arg}\` with invalid value "${value}". ` + error.message); } }); } catch (error) { report.error(`Field extension \`${name}\` on \`${typeName}.${fieldName}\` ` + `is not available.`); } }); }); } if (typeComposer.hasExtension(`addDefaultResolvers`)) { report.warn(`Deprecation warning - "noDefaultResolvers" is deprecated. In Gatsby 3, ` + `defined fields won't get resolvers, unless explicitly added with a ` + `directive/extension.`); } return typeComposer; }; const checkIsAllowedTypeName = name => { invariant(name !== `Node`, `The GraphQL type \`Node\` is reserved for internal use.`); invariant(!name.endsWith(`FilterInput`) && !name.endsWith(`SortInput`), `GraphQL type names ending with "FilterInput" or "SortInput" are ` + `reserved for internal use. Please rename \`${name}\`.`); invariant(![`Boolean`, `Date`, `Float`, `ID`, `Int`, `JSON`, `String`].includes(name), `The GraphQL type \`${name}\` is reserved for internal use by ` + `built-in scalar types.`); assertValidName(name); }; const createTypeComposerFromGatsbyType = ({ schemaComposer, type, parentSpan }) => { switch (type.kind) { case GatsbyGraphQLTypeKind.OBJECT: { return ObjectTypeComposer.createTemp(Object.assign({}, type.config, { interfaces: () => { if (type.config.interfaces) { return type.config.interfaces.map(iface => { if (typeof iface === `string`) { return schemaComposer.getIFTC(iface).getType(); } else { return iface; } }); } else { return []; } } }), schemaComposer); } case GatsbyGraphQLTypeKind.INPUT_OBJECT: { return InputTypeComposer.createTemp(type.config, schemaComposer); } case GatsbyGraphQLTypeKind.UNION: { return UnionTypeComposer.createTemp(Object.assign({}, type.config, { types: () => { if (type.config.types) { return type.config.types.map(typeName => schemaComposer.getOTC(typeName).getType()); } else { return []; } } }), schemaComposer); } case GatsbyGraphQLTypeKind.INTERFACE: { return InterfaceTypeComposer.createTemp(type.config, schemaComposer); } case GatsbyGraphQLTypeKind.ENUM: { return EnumTypeComposer.createTemp(type.config, schemaComposer); } case GatsbyGraphQLTypeKind.SCALAR: { return ScalarTypeComposer.createTemp(type.config, schemaComposer); } default: { report.warn(`Illegal type definition: ${JSON.stringify(type.config)}`); return null; } } }; const addSetFieldsOnGraphQLNodeTypeFields = ({ schemaComposer, nodeStore, parentSpan }) => Promise.all(Array.from(schemaComposer.values()).map(async tc => { if (tc instanceof ObjectTypeComposer && tc.hasInterface(`Node`)) { const typeName = tc.getTypeName(); const result = await apiRunner(`setFieldsOnGraphQLNodeType`, { type: { name: typeName, nodes: nodeStore.getNodesByType(typeName) }, traceId: `initial-setFieldsOnGraphQLNodeType`, parentSpan }); if (result) { // NOTE: `setFieldsOnGraphQLNodeType` only allows setting // nested fields with a path as property name, i.e. // `{ 'frontmatter.published': 'Boolean' }`, but not in the form // `{ frontmatter: { published: 'Boolean' }}` result.forEach(fields => tc.addNestedFields(fields)); } } })); const addThirdPartySchemas = ({ schemaComposer, thirdPartySchemas, parentSpan }) => { thirdPartySchemas.forEach(schema => { const schemaQueryType = schema.getQueryType(); const queryTC = schemaComposer.createTempTC(schemaQueryType); processThirdPartyTypeFields({ typeComposer: queryTC, schemaQueryType }); schemaComposer.Query.addFields(queryTC.getFields()); // Explicitly add the third-party schema's types, so they can be targeted // in `createResolvers` API. const types = schema.getTypeMap(); Object.keys(types).forEach(typeName => { const type = types[typeName]; if (type !== schemaQueryType && !isSpecifiedScalarType(type) && !isIntrospectionType(type) && type.name !== `Date` && type.name !== `JSON`) { const typeComposer = schemaComposer.createTC(type); if (typeComposer instanceof ObjectTypeComposer || typeComposer instanceof InterfaceTypeComposer) { processThirdPartyTypeFields({ typeComposer, schemaQueryType }); } typeComposer.setExtension(`createdFrom`, `thirdPartySchema`); schemaComposer.addSchemaMustHaveType(typeComposer); } }); }); }; const processThirdPartyTypeFields = ({ typeComposer, schemaQueryType }) => { // Fix for types that refer to Query. Thanks Relay Classic! typeComposer.getFieldNames().forEach(fieldName => { const field = typeComposer.getField(fieldName); const fieldType = field.type.toString(); if (fieldType.replace(/[[\]!]/g, ``) === schemaQueryType.name) { typeComposer.extendField(fieldName, { type: fieldType.replace(schemaQueryType.name, `Query`) }); } }); }; const addCustomResolveFunctions = async ({ schemaComposer, parentSpan }) => { const intermediateSchema = schemaComposer.buildSchema(); const createResolvers = resolvers => { Object.keys(resolvers).forEach(typeName => { const fields = resolvers[typeName]; if (schemaComposer.has(typeName)) { const tc = schemaComposer.getOTC(typeName); Object.keys(fields).forEach(fieldName => { const fieldConfig = fields[fieldName]; if (tc.hasField(fieldName)) { const originalFieldConfig = tc.getFieldConfig(fieldName); const originalTypeName = originalFieldConfig.type.toString(); const originalResolver = originalFieldConfig.resolve; let fieldTypeName; if (fieldConfig.type) { fieldTypeName = Array.isArray(fieldConfig.type) ? stringifyArray(fieldConfig.type) : fieldConfig.type.toString(); } if (!fieldTypeName || fieldTypeName.replace(/!/g, ``) === originalTypeName.replace(/!/g, ``) || tc.getExtension(`createdFrom`) === `thirdPartySchema`) { const newConfig = {}; if (fieldConfig.type) { newConfig.type = fieldConfig.type; } if (fieldConfig.args) { newConfig.args = fieldConfig.args; } if (fieldConfig.resolve) { newConfig.resolve = (source, args, context, info) => fieldConfig.resolve(source, args, context, Object.assign({}, info, { originalResolver: originalResolver || context.defaultFieldResolver })); } tc.extendField(fieldName, newConfig); } else if (fieldTypeName) { report.warn(`\`createResolvers\` passed resolvers for field ` + `\`${typeName}.${fieldName}\` with type \`${fieldTypeName}\`. ` + `Such a field with type \`${originalTypeName}\` already exists ` + `on the type. Use \`createTypes\` to override type fields.`); } } else { tc.addFields({ [fieldName]: fieldConfig }); } }); } else { report.warn(`\`createResolvers\` passed resolvers for type \`${typeName}\` that ` + `doesn't exist in the schema. Use \`createTypes\` to add the type ` + `before adding resolvers.`); } }); }; await apiRunner(`createResolvers`, { intermediateSchema, createResolvers, traceId: `initial-createResolvers`, parentSpan }); }; const addConvenienceChildrenFields = ({ schemaComposer }) => { const parentTypesToChildren = new Map(); const mimeTypesToChildren = new Map(); const typesHandlingMimeTypes = new Map(); schemaComposer.forEach(type => { if ((type instanceof ObjectTypeComposer || type instanceof InterfaceTypeComposer) && type.hasExtension(`mimeTypes`)) { const { types } = type.getExtension(`mimeTypes`); new Set(types).forEach(mimeType => { if (!typesHandlingMimeTypes.has(mimeType)) { typesHandlingMimeTypes.set(mimeType, new Set()); } typesHandlingMimeTypes.get(mimeType).add(type); }); } if ((type instanceof ObjectTypeComposer || type instanceof InterfaceTypeComposer) && type.hasExtension(`childOf`)) { if (type instanceof ObjectTypeComposer && !type.hasInterface(`Node`)) { report.error(`The \`childOf\` extension can only be used on types that implement the \`Node\` interface.\n` + `Check the type definition of \`${type.getTypeName()}\`.`); return; } if (type instanceof InterfaceTypeComposer && !type.hasExtension(`nodeInterface`)) { report.error(`The \`childOf\` extension can only be used on interface types that ` + `have the \`@nodeInterface\` extension.\n` + `Check the type definition of \`${type.getTypeName()}\`.`); return; } const { types, mimeTypes, many } = type.getExtension(`childOf`); new Set(types).forEach(parentType => { if (!parentTypesToChildren.has(parentType)) { parentTypesToChildren.set(parentType, new Map()); } parentTypesToChildren.get(parentType).set(type, many); }); new Set(mimeTypes).forEach(mimeType => { if (!mimeTypesToChildren.has(mimeType)) { mimeTypesToChildren.set(mimeType, new Map()); } mimeTypesToChildren.get(mimeType).set(type, many); }); } }); parentTypesToChildren.forEach((children, parent) => { if (!schemaComposer.has(parent)) return; const typeComposer = schemaComposer.getAnyTC(parent); if (typeComposer instanceof InterfaceTypeComposer && !typeComposer.hasExtension(`nodeInterface`)) { report.error(`With the \`childOf\` extension, children fields can only be added to ` + `interfaces which have the \`@nodeInterface\` extension.\n` + `Check the type definition of \`${typeComposer.getTypeName()}\`.`); return; } children.forEach((many, child) => { if (many) { typeComposer.addFields(createChildrenField(child.getTypeName())); } else { typeComposer.addFields(createChildField(child.getTypeName())); } }); }); mimeTypesToChildren.forEach((children, mimeType) => { const parentTypes = typesHandlingMimeTypes.get(mimeType); if (parentTypes) { parentTypes.forEach(typeComposer => { if (typeComposer instanceof InterfaceTypeComposer && !typeComposer.hasExtension(`nodeInterface`)) { report.error(`With the \`childOf\` extension, children fields can only be added to ` + `interfaces which have the \`@nodeInterface\` extension.\n` + `Check the type definition of \`${typeComposer.getTypeName()}\`.`); return; } children.forEach((many, child) => { if (many) { typeComposer.addFields(createChildrenField(child.getTypeName())); } else { typeComposer.addFields(createChildField(child.getTypeName())); } }); }); } }); }; const addImplicitConvenienceChildrenFields = ({ schemaComposer, typeComposer, nodeStore }) => { const shouldInfer = typeComposer.getExtension(`infer`); // In Gatsby v3, when `@dontInfer` is set, children fields will not be // created for parent-child relations set by plugins with // `createParentChildLink`. With `@dontInfer`, only parent-child // relations explicitly set with the `childOf` extension will be added. // if (shouldInfer === false) return const parentTypeName = typeComposer.getTypeName(); const nodes = nodeStore.getNodesByType(parentTypeName); const childNodesByType = groupChildNodesByType({ nodeStore, nodes }); Object.keys(childNodesByType).forEach(typeName => { const typeChildren = childNodesByType[typeName]; const maxChildCount = _.maxBy(_.values(_.groupBy(typeChildren, c => c.parent)), g => g.length).length; // Adding children fields to types with the `@dontInfer` extension is deprecated if (shouldInfer === false) { const childTypeComposer = schemaComposer.getAnyTC(typeName); const childOfExtension = childTypeComposer.getExtension(`childOf`); const many = maxChildCount > 1; // Only warn when the parent-child relation has not been explicitly set with if (!childOfExtension || !childOfExtension.types.includes(parentTypeName) || !childOfExtension.many === many) { const fieldName = _.camelCase(`${many ? `children` : `child`} ${typeName}`); report.warn(`On types with the \`@dontInfer\` directive, or with the \`infer\` ` + `extension set to \`false\`, automatically adding fields for ` + `children types is deprecated.\n` + `In Gatsby v3, only children fields explicitly set with the ` + `\`childOf\` extension will be added.\n` + `For example, in Gatsby v3, \`${parentTypeName}\` will ` + `not get a \`${fieldName}\` field.`); } } if (maxChildCount > 1) { typeComposer.addFields(createChildrenField(typeName)); } else { typeComposer.addFields(createChildField(typeName)); } }); }; const createChildrenField = typeName => { return { [_.camelCase(`children ${typeName}`)]: { type: () => [typeName], resolve(source, args, context) { const { path } = context; return context.nodeModel.getNodesByIds({ ids: source.children, type: typeName }, { path }); } } }; }; const createChildField = typeName => { return { [_.camelCase(`child ${typeName}`)]: { type: () => typeName, async resolve(source, args, context) { const { path } = context; const result = await context.nodeModel.getNodesByIds({ ids: source.children, type: typeName }, { path }); if (result && result.length > 0) { return result[0]; } else { return null; } } } }; }; const groupChildNodesByType = ({ nodeStore, nodes }) => _(nodes).flatMap(node => (node.children || []).map(nodeStore.getNode)).groupBy(node => node.internal ? node.internal.type : undefined).value(); const addTypeToRootQuery = ({ schemaComposer, typeComposer }) => { // TODO: We should have an abstraction for keeping and clearing // related TypeComposers and InputTypeComposers. // Also see the comment on the skipped test in `rebuild-schema`. typeComposer.removeInputTypeComposer(); const sortInputTC = getSortInput({ schemaComposer, typeComposer }); const filterInputTC = getFilterInput({ schemaComposer, typeComposer }); const paginationTC = getPagination({ schemaComposer, typeComposer }); const typeName = typeComposer.getTypeName(); // not strictly correctly, result is `npmPackage` and `allNpmPackage` from type `NPMPackage` const queryName = _.camelCase(typeName); const queryNamePlural = _.camelCase(`all ${typeName}`); schemaComposer.Query.addFields({ [queryName]: { type: typeComposer, args: Object.assign({}, filterInputTC.getFields()), resolve: findOne(typeName) }, [queryNamePlural]: { type: paginationTC, args: { filter: filterInputTC, sort: sortInputTC, skip: `Int`, limit: `Int` }, resolve: findManyPaginated(typeName) } }).makeFieldNonNull(queryNamePlural); }; const parseTypes = ({ doc, plugin, createdFrom, schemaComposer, parentSpan }) => { const types = []; doc.definitions.forEach(def => { const name = def.name.value; checkIsAllowedTypeName(name); if (schemaComposer.has(name)) { // We don't check if ast.kind matches composer type, but rely // that this will throw when something is wrong and get // reported by `reportParsingError`. // Keep the original type composer around const typeComposer = schemaComposer.get(name); // After this, the parsed type composer will be registered as the composer // handling the type name const parsedType = schemaComposer.typeMapper.makeSchemaDef(def); // Merge the parsed type with the original mergeTypes({ schemaComposer, typeComposer, type: parsedType, plugin, createdFrom, parentSpan }); // Set the original type composer (with the merged fields added) // as the correct composer for the type name schemaComposer.typeMapper.set(typeComposer.getTypeName(), typeComposer); } else { const parsedType = schemaComposer.typeMapper.makeSchemaDef(def); types.push(parsedType); } }); return types; }; const parseTypeDefs = ({ typeDefs, plugin, createdFrom, schemaComposer, parentSpan }) => { const doc = parse(typeDefs); return parseTypes({ doc, plugin, createdFrom, schemaComposer, parentSpan }); }; const reportParsingError = error => { const { message, source, locations } = error; if (source && locations && locations.length) { const { codeFrameColumns } = require(`@babel/code-frame`); const frame = codeFrameColumns(source.body, { start: locations[0] }, { linesAbove: 5, linesBelow: 5 }); report.panic(`Encountered an error parsing the provided GraphQL type definitions:\n` + message + `\n\n` + frame + `\n`); } else { throw error; } }; const stringifyArray = arr => `[${arr.map(item => Array.isArray(item) ? stringifyArray(item) : item.toString())}]`; // TODO: Import this directly from graphql-compose once we update to v7 const isNamedTypeComposer = type => type instanceof ObjectTypeComposer || type instanceof InputTypeComposer || type instanceof ScalarTypeComposer || type instanceof EnumTypeComposer || type instanceof InterfaceTypeComposer || type instanceof UnionTypeComposer; const validate = (type, value) => { if (type instanceof GraphQLNonNull) { if (value == null) { throw new Error(`Expected non-null field value.`); } return validate(type.ofType, value); } else if (type instanceof GraphQLList) { if (!Array.isArray(value)) { throw new Error(`Expected array field value.`); } return value.map(v => validate(type.ofType, v)); } else { return type.parseValue(value); } }; const checkQueryableInterfaces = ({ schemaComposer }) => { const queryableInterfaces = new Set(); schemaComposer.forEach(type => { if (type instanceof InterfaceTypeComposer && type.getExtension(`nodeInterface`)) { queryableInterfaces.add(type.getTypeName()); } }); const incorrectTypes = []; schemaComposer.forEach(type => { if (type instanceof ObjectTypeComposer) { const interfaces = type.getInterfaces(); if (interfaces.some(iface => queryableInterfaces.has(iface.name)) && !type.hasInterface(`Node`)) { incorrectTypes.push(type.getTypeName()); } } }); if (incorrectTypes.length) { report.panic(`Interfaces with the \`nodeInterface\` extension must only be ` + `implemented by types which also implement the \`Node\` ` + `interface. Check the type definition of ` + `${incorrectTypes.map(t => `\`${t}\``).join(`, `)}.`); } }; //# sourceMappingURL=schema.js.map