composition/src/schema-building/ast.ts (264 lines of code) (raw):

import { ConstDirectiveNode, ConstValueNode, DirectiveDefinitionNode, EnumValueDefinitionNode, FieldDefinitionNode, InputValueDefinitionNode, InterfaceTypeDefinitionNode, InterfaceTypeExtensionNode, Kind, NamedTypeNode, NameNode, ObjectTypeDefinitionNode, ObjectTypeExtensionNode, StringValueNode, TypeNode, } from 'graphql'; import { formatDescription, stringToNameNode } from '../ast/utils'; import { maximumTypeNestingExceededError, unexpectedTypeNodeKindFatalError } from '../errors/errors'; import { MAXIMUM_TYPE_NESTING } from '../utils/integer-constants'; export type MutableDirectiveDefinitionNode = { arguments: MutableInputValueNode[]; kind: Kind.DIRECTIVE_DEFINITION; locations: NameNode[]; name: NameNode; repeatable: boolean; description?: StringValueNode; }; export function getMutableDirectiveDefinitionNode(node: DirectiveDefinitionNode): MutableDirectiveDefinitionNode { return { arguments: [], kind: node.kind, locations: [], name: { ...node.name }, repeatable: node.repeatable, description: formatDescription(node.description), }; } export type MutableEnumNode = { kind: Kind.ENUM_TYPE_DEFINITION; name: NameNode; description?: StringValueNode; directives?: ConstDirectiveNode[]; values?: MutableEnumValueNode[]; }; export function getMutableEnumNode(nameNode: NameNode): MutableEnumNode { return { kind: Kind.ENUM_TYPE_DEFINITION, name: { ...nameNode }, }; } export type MutableEnumValueNode = { directives: ConstDirectiveNode[]; // always initialise for ease kind: Kind.ENUM_VALUE_DEFINITION; name: NameNode; description?: StringValueNode; }; export function getMutableEnumValueNode(node: EnumValueDefinitionNode): MutableEnumValueNode { return { directives: [], kind: node.kind, name: { ...node.name }, description: formatDescription(node.description), }; } export type MutableFieldNode = { arguments: MutableInputValueNode[]; // always initialise for ease directives: ConstDirectiveNode[]; // always initialise for ease kind: Kind.FIELD_DEFINITION; name: NameNode; type: MutableTypeNode; description?: StringValueNode; }; export function getMutableFieldNode(node: FieldDefinitionNode, hostPath: string, errors: Error[]): MutableFieldNode { return { arguments: [], directives: [], kind: node.kind, name: { ...node.name }, type: getMutableTypeNode(node.type, hostPath, errors), description: formatDescription(node.description), }; } export type MutableInputObjectNode = { kind: Kind.INPUT_OBJECT_TYPE_DEFINITION; name: NameNode; description?: StringValueNode; directives?: ConstDirectiveNode[]; fields?: InputValueDefinitionNode[]; }; export function getMutableInputObjectNode(nameNode: NameNode): MutableInputObjectNode { return { kind: Kind.INPUT_OBJECT_TYPE_DEFINITION, name: { ...nameNode }, }; } export type MutableInputValueNode = { directives: ConstDirectiveNode[]; // always initialise for ease kind: Kind.INPUT_VALUE_DEFINITION; name: NameNode; type: MutableTypeNode; defaultValue?: ConstValueNode; description?: StringValueNode; }; export function getMutableInputValueNode( node: InputValueDefinitionNode, hostPath: string, errors: Error[], ): MutableInputValueNode { return { directives: [], kind: node.kind, name: { ...node.name }, type: getMutableTypeNode(node.type, hostPath, errors), defaultValue: node.defaultValue, description: formatDescription(node.description), }; } export type MutableInterfaceNode = { kind: Kind.INTERFACE_TYPE_DEFINITION; name: NameNode; description?: StringValueNode; directives?: ConstDirectiveNode[]; fields?: FieldDefinitionNode[]; interfaces?: NamedTypeNode[]; }; export function getMutableInterfaceNode(nameNode: NameNode): MutableInterfaceNode { return { kind: Kind.INTERFACE_TYPE_DEFINITION, name: { ...nameNode }, }; } export type MutableObjectNode = { kind: Kind.OBJECT_TYPE_DEFINITION; name: NameNode; description?: StringValueNode; directives?: ConstDirectiveNode[]; fields?: FieldDefinitionNode[]; interfaces?: NamedTypeNode[]; }; export function getMutableObjectNode(nameNode: NameNode): MutableObjectNode { return { kind: Kind.OBJECT_TYPE_DEFINITION, name: { ...nameNode }, }; } export type MutableObjectExtensionNode = { kind: Kind.OBJECT_TYPE_EXTENSION; name: NameNode; description?: StringValueNode; // @extends directive would allow for a description directives?: ConstDirectiveNode[]; fields?: FieldDefinitionNode[]; interfaces?: NamedTypeNode[]; }; export function getMutableObjectExtensionNode( node: ObjectTypeDefinitionNode | ObjectTypeExtensionNode, ): MutableObjectExtensionNode { const description = node.kind === Kind.OBJECT_TYPE_DEFINITION ? node.description : undefined; return { kind: Kind.OBJECT_TYPE_EXTENSION, name: { ...node.name }, description: formatDescription(description), }; } export type MutableScalarNode = { kind: Kind.SCALAR_TYPE_DEFINITION; name: NameNode; description?: StringValueNode; directives?: ConstDirectiveNode[]; }; export function getMutableScalarNode(nameNode: NameNode): MutableScalarNode { return { kind: Kind.SCALAR_TYPE_DEFINITION, name: { ...nameNode }, }; } // This type allows the building of a MutableTypeNode export type MutableIntermediateTypeNode = { kind: Kind.NAMED_TYPE | Kind.LIST_TYPE | Kind.NON_NULL_TYPE; name?: NameNode; type?: MutableIntermediateTypeNode; }; export type MutableTypeNode = MutableNamedTypeNode | MutableListTypeNode | MutableNonNullTypeNode; export type MutableNamedTypeNode = { kind: Kind.NAMED_TYPE; name: NameNode; }; export type MutableListTypeNode = { kind: Kind.LIST_TYPE; type: MutableTypeNode; }; export type MutableNonNullTypeNode = { kind: Kind.NON_NULL_TYPE; type: MutableNamedTypeNode | MutableListTypeNode; }; export function getMutableTypeNode(node: TypeNode, typePath: string, errors: Error[]): MutableTypeNode { const deepCopy: MutableIntermediateTypeNode = { kind: node.kind }; let lastTypeNode = deepCopy; for (let i = 0; i < MAXIMUM_TYPE_NESTING; i++) { switch (node.kind) { case Kind.NAMED_TYPE: lastTypeNode.name = { ...node.name }; return deepCopy as MutableTypeNode; case Kind.LIST_TYPE: lastTypeNode.kind = node.kind; lastTypeNode.type = { kind: node.type.kind }; lastTypeNode = lastTypeNode.type; node = node.type; continue; case Kind.NON_NULL_TYPE: lastTypeNode.kind = node.kind; lastTypeNode.type = { kind: node.type.kind }; lastTypeNode = lastTypeNode.type; node = node.type; continue; default: throw unexpectedTypeNodeKindFatalError(typePath); } } errors.push(maximumTypeNestingExceededError(typePath)); // Return a dummy type when the type has exceeded nesting return { kind: Kind.NAMED_TYPE, name: stringToNameNode(getTypeNodeNamedTypeName(node)) }; } export type MutableUnionNode = { kind: Kind.UNION_TYPE_DEFINITION; name: NameNode; description?: StringValueNode; directives?: ConstDirectiveNode[]; types?: NamedTypeNode[]; }; export function getMutableUnionNode(nameNode: NameNode): MutableUnionNode { return { kind: Kind.UNION_TYPE_DEFINITION, name: { ...nameNode }, }; } export type MutableTypeDefinitionNode = | MutableDirectiveDefinitionNode | MutableEnumNode | MutableInputObjectNode | MutableInterfaceNode | MutableObjectNode | MutableScalarNode | MutableUnionNode; export type CompositeOutputNode = | InterfaceTypeDefinitionNode | InterfaceTypeExtensionNode | ObjectTypeDefinitionNode | ObjectTypeExtensionNode; export function getTypeNodeNamedTypeName(typeNode: TypeNode): string { switch (typeNode.kind) { case Kind.NAMED_TYPE: return typeNode.name.value; case Kind.LIST_TYPE: // intentional fallthrough case Kind.NON_NULL_TYPE: return getTypeNodeNamedTypeName(typeNode.type); } } export function getNamedTypeNode(typeNode: TypeNode): TypeNode { switch (typeNode.kind) { case Kind.NAMED_TYPE: return typeNode; default: return getNamedTypeNode(typeNode.type); } }