UNPKG

@cran/lib.gql.core

Version:

GQL Core Functionality

144 lines (117 loc) 4.97 kB
import type { Plugin } from "../plugins/Plugin"; import { MapperKind, type SchemaMapper, getDirective, mapSchema, } from "@graphql-tools/utils"; import { isDirective, isSchema } from "graphql"; import { parseLiteral } from "./parse"; export type SchemaMapperType<Kind extends MapperKind | null = null> = SchemaMapperArgs<Kind>[0]; export type SchemaMapperTypes = { [Kind in MapperKind]: SchemaMapperType<Kind>; }; export type SchemaMapperArgs<Kind extends MapperKind | null = null> = Parameters<NonNullable<Kind extends MapperKind ? SchemaMapper[Kind] : SchemaMapper[keyof SchemaMapper]>>; export type SchemaMapperFunction<Kind extends MapperKind | null = null> = ( directives: Array<Record<string, unknown>>, ...original: SchemaMapperArgs<Kind> // eslint-disable-next-line @typescript-eslint/no-invalid-void-type ) => SchemaMapperType<Kind> | null | undefined | void; export type SchemaMapperExtended = { [Kind in MapperKind]: SchemaMapperFunction<Kind>; }; const Location: { [Key in MapperKind]: Array<string>; } = { [MapperKind.TYPE] : [ "SCALAR", "OBJECT", "INTERFACE", "UNION", "ENUM", "INPUT_OBJECT", ], [MapperKind.SCALAR_TYPE] : [ "SCALAR", ], [MapperKind.ENUM_TYPE] : [ "ENUM", ], [MapperKind.COMPOSITE_TYPE] : [ "OBJECT", "INTERFACE", "UNION", ], [MapperKind.OBJECT_TYPE] : [ "OBJECT", ], [MapperKind.INPUT_OBJECT_TYPE] : [ "INPUT_OBJECT", ], [MapperKind.ABSTRACT_TYPE] : [ "INTERFACE", "UNION", ], [MapperKind.UNION_TYPE] : [ "UNION_TYPE", ], [MapperKind.INTERFACE_TYPE] : [ "INTERFACE", ], [MapperKind.ROOT_OBJECT] : [ "SCHEMA", ], [MapperKind.QUERY] : [ "OBJECT", ], [MapperKind.MUTATION] : [ "OBJECT", ], [MapperKind.SUBSCRIPTION] : [ "OBJECT", ], [MapperKind.ENUM_VALUE] : [ "ENUM_VALUE", ], [MapperKind.FIELD] : [ "FIELD_DEFINITION", "INPUT_FIELD_DEFINITION", ], [MapperKind.OBJECT_FIELD] : [ "FIELD_DEFINITION", ], [MapperKind.ROOT_FIELD] : [ "FIELD_DEFINITION", ], [MapperKind.QUERY_ROOT_FIELD] : [ "FIELD_DEFINITION", ], [MapperKind.MUTATION_ROOT_FIELD] : [ "FIELD_DEFINITION", ], [MapperKind.SUBSCRIPTION_ROOT_FIELD]: [ "FIELD_DEFINITION", ], [MapperKind.INTERFACE_FIELD] : [ "FIELD_DEFINITION", ], [MapperKind.COMPOSITE_FIELD] : [ "FIELD_DEFINITION", ], [MapperKind.ARGUMENT] : [ "ARUGMENT_DEFINITION", ], [MapperKind.INPUT_OBJECT_FIELD] : [ "INPUT_FIELD_DEFINITION", ], [MapperKind.DIRECTIVE] : [ "DIRECTIVE", ], }; function gatherLocations ( keys: Array<MapperKind> ) { const locations = new Set<string>(); for (const key of keys) { for (const location of Location[key]) { locations.add(location); } } return ([ ...locations, ]).join("|"); } export function createDirective ( name: string, args: Record<string, string>, mapper: Partial<SchemaMapperExtended> ) { const argList = Object.entries(args) .map(function mapArg ( [ key, value, ] ) { return `${key}:${value}`; }).join(","); const argStruct = argList ? `(${argList})` : ""; return { typeDefs: `directive @${name}${argStruct} on ${ gatherLocations(Object.keys(mapper) as Array<MapperKind>) }`, transformer ( schema ) { return mapSchema(schema, wrapSchemaMapper(name, mapper)); }, } as const as Plugin; } function wrapSchemaMapper ( name: string, mapper: Partial<SchemaMapperExtended> ) { const wrapped: Record<string, unknown> = { }; for (const key in mapper) { wrapped[key] = wrapMapper( name, mapper[key as keyof SchemaMapperExtended]! as SchemaMapperFunction ); } return wrapped as SchemaMapper; } function wrapMapper ( name: string, mapperFunction: SchemaMapperFunction ) { return function mapper ( ...args: SchemaMapperArgs ) { const [ type, ] = args; if (isDirective(type)) { return mapperFunction([ ], ...args) ?? type; } const directives = getDirective( (args as Array<unknown>).find(isSchema)!, type, name ) ?? getAstDirective(type, name); if (directives?.[0]) { return mapperFunction(directives, ...args) ?? type; } return type; }; } function getAstDirective ( type: SchemaMapperType, name: string ) { if (type.astNode && "directives" in type.astNode && type.astNode.directives) { return type.astNode.directives.filter(function filterDir ( directive ) { return directive.name.value === name; }).map(function mapDir ( directive ) { return directive.arguments ?.reduce<Record<string, unknown>>(function reduceArg ( acc, arg ) { const { name: { value: argName, }, value, } = arg; acc[argName] = parseLiteral("value" in value ? String(value.value) : "null", value); return acc; }, { }) ?? { }; }); } return [ ]; }