UNPKG

codemirror-graphql

Version:
190 lines (182 loc) 5.66 kB
/** * Copyright (c) 2021 GraphQL Contributors * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. */ import { isCompositeType, getNullableType, getNamedType, GraphQLEnumType, GraphQLInputObjectType, GraphQLList, GraphQLSchema, GraphQLType, GraphQLObjectType, GraphQLField, GraphQLDirective, GraphQLArgument, GraphQLInputType, GraphQLEnumValue, GraphQLInputFieldMap, SchemaMetaFieldDef, TypeMetaFieldDef, TypeNameMetaFieldDef, } from 'graphql'; import type { State, Maybe } from 'graphql-language-service'; import forEachState from './forEachState'; export interface TypeInfo { schema: GraphQLSchema; type?: Maybe<GraphQLType>; parentType?: Maybe<GraphQLType>; inputType?: Maybe<GraphQLInputType>; directiveDef?: Maybe<GraphQLDirective>; fieldDef?: Maybe<GraphQLField<any, any>>; argDef?: Maybe<GraphQLArgument>; argDefs?: Maybe<GraphQLArgument[]>; enumValue?: Maybe<GraphQLEnumValue>; objectFieldDefs?: Maybe<GraphQLInputFieldMap>; } /** * Utility for collecting rich type information given any token's state * from the graphql-mode parser. */ export default function getTypeInfo(schema: GraphQLSchema, tokenState: State) { const info: TypeInfo = { schema, type: null, parentType: null, inputType: null, directiveDef: null, fieldDef: null, argDef: null, argDefs: null, objectFieldDefs: null, }; forEachState(tokenState, (state: State) => { switch (state.kind) { case 'Query': case 'ShortQuery': info.type = schema.getQueryType(); break; case 'Mutation': info.type = schema.getMutationType(); break; case 'Subscription': info.type = schema.getSubscriptionType(); break; case 'InlineFragment': case 'FragmentDefinition': if (state.type) { info.type = schema.getType(state.type); } break; case 'Field': case 'AliasedField': info.fieldDef = info.type && state.name ? getFieldDef(schema, info.parentType, state.name) : null; info.type = info.fieldDef?.type; break; case 'SelectionSet': info.parentType = info.type ? getNamedType(info.type) : null; break; case 'Directive': info.directiveDef = state.name ? schema.getDirective(state.name) : null; break; case 'Arguments': const parentDef = state.prevState ? state.prevState.kind === 'Field' ? info.fieldDef : state.prevState.kind === 'Directive' ? info.directiveDef : state.prevState.kind === 'AliasedField' ? state.prevState.name && getFieldDef(schema, info.parentType, state.prevState.name) : null : null; info.argDefs = parentDef ? (parentDef.args as GraphQLArgument[]) : null; break; case 'Argument': info.argDef = null; if (info.argDefs) { for (let i = 0; i < info.argDefs.length; i++) { if (info.argDefs[i].name === state.name) { info.argDef = info.argDefs[i]; break; } } } info.inputType = info.argDef?.type; break; case 'EnumValue': const enumType = info.inputType ? getNamedType(info.inputType) : null; info.enumValue = enumType instanceof GraphQLEnumType ? find( enumType.getValues() as GraphQLEnumValue[], val => val.value === state.name, ) : null; break; case 'ListValue': const nullableType = info.inputType ? getNullableType(info.inputType) : null; info.inputType = nullableType instanceof GraphQLList ? nullableType.ofType : null; break; case 'ObjectValue': const objectType = info.inputType ? getNamedType(info.inputType) : null; info.objectFieldDefs = objectType instanceof GraphQLInputObjectType ? objectType.getFields() : null; break; case 'ObjectField': const objectField = state.name && info.objectFieldDefs ? info.objectFieldDefs[state.name] : null; info.inputType = objectField?.type; // @ts-expect-error info.fieldDef = objectField; break; case 'NamedType': info.type = state.name ? schema.getType(state.name) : null; break; } }); return info; } // Gets the field definition given a type and field name function getFieldDef( schema: GraphQLSchema, type: Maybe<GraphQLType>, fieldName: string, ) { if (fieldName === SchemaMetaFieldDef.name && schema.getQueryType() === type) { return SchemaMetaFieldDef; } if (fieldName === TypeMetaFieldDef.name && schema.getQueryType() === type) { return TypeMetaFieldDef; } if (fieldName === TypeNameMetaFieldDef.name && isCompositeType(type)) { return TypeNameMetaFieldDef; } if (type && (type as GraphQLObjectType).getFields) { return (type as GraphQLObjectType).getFields()[fieldName]; } } // Returns the first item in the array which causes predicate to return truthy. function find<T>(array: T[], predicate: (item: T) => boolean) { for (let i = 0; i < array.length; i++) { if (predicate(array[i])) { return array[i]; } } }