UNPKG

megaera

Version:
118 lines (117 loc) 4.14 kB
import { isNonNullType, } from 'graphql/type/definition.js'; import { typeFromAST, TypeInfo, visitWithTypeInfo, } from 'graphql/utilities/index.js'; import { parse, print, visit } from 'graphql/language/index.js'; import { GraphQLError } from 'graphql/error/index.js'; import { firstLetterUpper } from './utils.js'; export function traverse(schema, source) { const ast = parse(source); const typeInfo = new TypeInfo(schema); const content = { operations: [], fragments: new Map(), }; const stack = []; const visitor = visitWithTypeInfo(typeInfo, { OperationDefinition: { enter: function (node) { if (node.name === undefined) { throw new GraphQLError(firstLetterUpper(node.operation) + ' name is required', node, source); } checkUnique(node.name.value, content); const variables = []; for (const v of node.variableDefinitions ?? []) { const type = typeFromAST(schema, v.type); variables.push({ name: v.variable.name.value, type: type, required: v.defaultValue === undefined && isNonNullType(type), }); } const s = { name: node.name.value, type: typeInfo.getType() ?? undefined, fields: [], inlineFragments: [], variables: variables, source: print(node), }; stack.push(s); content.operations.push(s); }, leave() { stack.pop(); }, }, FragmentDefinition: { enter(node) { checkUnique(node.name.value, content); const s = { name: node.name.value, type: typeInfo.getType() ?? undefined, fields: [], inlineFragments: [], source: print(node), }; stack.push(s); content.fragments.set(s.name, s); }, leave() { stack.pop(); }, }, Field: { enter(node) { const s = { name: node.alias?.value ?? node.name.value, type: typeInfo.getType() ?? undefined, fields: [], inlineFragments: [], }; stack.at(-1)?.fields.push(s); stack.push(s); }, leave() { stack.pop(); }, }, FragmentSpread: { enter(node) { stack.at(-1)?.fields.push({ name: node.name.value, type: typeInfo.getType() ?? undefined, isFragment: true, fields: [], inlineFragments: [], }); }, }, InlineFragment: { enter(node) { if (!node.typeCondition) { throw new GraphQLError('Inline fragment must have type condition.', node, source); } const s = { name: node.typeCondition.name.value, type: typeInfo.getType() ?? undefined, fields: [], inlineFragments: [], }; stack.at(-1)?.inlineFragments.push(s); stack.push(s); }, leave() { stack.pop(); }, }, }); visit(ast, visitor); return content; } function checkUnique(name, content) { if (content.operations.find((o) => o.name === name)) { throw new GraphQLError(`Operation with name "${name}" is already defined.`); } if (content.fragments.has(name)) { throw new GraphQLError(`Fragment with name "${name}" is already defined.`); } }