summaryrefslogtreecommitdiff
path: root/school/node_modules/graphql/utilities/extendSchema.js.flow
diff options
context:
space:
mode:
Diffstat (limited to 'school/node_modules/graphql/utilities/extendSchema.js.flow')
-rw-r--r--school/node_modules/graphql/utilities/extendSchema.js.flow782
1 files changed, 782 insertions, 0 deletions
diff --git a/school/node_modules/graphql/utilities/extendSchema.js.flow b/school/node_modules/graphql/utilities/extendSchema.js.flow
new file mode 100644
index 0000000..2d5a229
--- /dev/null
+++ b/school/node_modules/graphql/utilities/extendSchema.js.flow
@@ -0,0 +1,782 @@
+// @flow strict
+import objectValues from '../polyfills/objectValues';
+
+import keyMap from '../jsutils/keyMap';
+import inspect from '../jsutils/inspect';
+import mapValue from '../jsutils/mapValue';
+import invariant from '../jsutils/invariant';
+import devAssert from '../jsutils/devAssert';
+
+import type { DirectiveLocationEnum } from '../language/directiveLocation';
+import type {
+ Location,
+ DocumentNode,
+ StringValueNode,
+ TypeNode,
+ NamedTypeNode,
+ SchemaDefinitionNode,
+ SchemaExtensionNode,
+ TypeDefinitionNode,
+ InterfaceTypeDefinitionNode,
+ InterfaceTypeExtensionNode,
+ ObjectTypeDefinitionNode,
+ ObjectTypeExtensionNode,
+ UnionTypeDefinitionNode,
+ UnionTypeExtensionNode,
+ FieldDefinitionNode,
+ InputObjectTypeDefinitionNode,
+ InputObjectTypeExtensionNode,
+ InputValueDefinitionNode,
+ EnumTypeDefinitionNode,
+ EnumTypeExtensionNode,
+ EnumValueDefinitionNode,
+ DirectiveDefinitionNode,
+ ScalarTypeDefinitionNode,
+ ScalarTypeExtensionNode,
+} from '../language/ast';
+import { Kind } from '../language/kinds';
+import { TokenKind } from '../language/tokenKind';
+import { dedentBlockStringValue } from '../language/blockString';
+import {
+ isTypeDefinitionNode,
+ isTypeExtensionNode,
+} from '../language/predicates';
+
+import { assertValidSDLExtension } from '../validation/validate';
+
+import { getDirectiveValues } from '../execution/values';
+
+import type {
+ GraphQLSchemaValidationOptions,
+ GraphQLSchemaNormalizedConfig,
+} from '../type/schema';
+import type {
+ GraphQLType,
+ GraphQLNamedType,
+ GraphQLFieldConfig,
+ GraphQLFieldConfigMap,
+ GraphQLArgumentConfig,
+ GraphQLFieldConfigArgumentMap,
+ GraphQLEnumValueConfigMap,
+ GraphQLInputFieldConfigMap,
+} from '../type/definition';
+import { assertSchema, GraphQLSchema } from '../type/schema';
+import { specifiedScalarTypes, isSpecifiedScalarType } from '../type/scalars';
+import { introspectionTypes, isIntrospectionType } from '../type/introspection';
+import {
+ GraphQLDirective,
+ GraphQLDeprecatedDirective,
+ GraphQLSpecifiedByDirective,
+} from '../type/directives';
+import {
+ isScalarType,
+ isObjectType,
+ isInterfaceType,
+ isUnionType,
+ isListType,
+ isNonNullType,
+ isEnumType,
+ isInputObjectType,
+ GraphQLList,
+ GraphQLNonNull,
+ GraphQLScalarType,
+ GraphQLObjectType,
+ GraphQLInterfaceType,
+ GraphQLUnionType,
+ GraphQLEnumType,
+ GraphQLInputObjectType,
+} from '../type/definition';
+
+import { valueFromAST } from './valueFromAST';
+
+type Options = {|
+ ...GraphQLSchemaValidationOptions,
+
+ /**
+ * Descriptions are defined as preceding string literals, however an older
+ * experimental version of the SDL supported preceding comments as
+ * descriptions. Set to true to enable this deprecated behavior.
+ * This option is provided to ease adoption and will be removed in v16.
+ *
+ * Default: false
+ */
+ commentDescriptions?: boolean,
+
+ /**
+ * Set to true to assume the SDL is valid.
+ *
+ * Default: false
+ */
+ assumeValidSDL?: boolean,
+|};
+
+/**
+ * Produces a new schema given an existing schema and a document which may
+ * contain GraphQL type extensions and definitions. The original schema will
+ * remain unaltered.
+ *
+ * Because a schema represents a graph of references, a schema cannot be
+ * extended without effectively making an entire copy. We do not know until it's
+ * too late if subgraphs remain unchanged.
+ *
+ * This algorithm copies the provided schema, applying extensions while
+ * producing the copy. The original schema remains unaltered.
+ *
+ * Accepts options as a third argument:
+ *
+ * - commentDescriptions:
+ * Provide true to use preceding comments as the description.
+ *
+ */
+export function extendSchema(
+ schema: GraphQLSchema,
+ documentAST: DocumentNode,
+ options?: Options,
+): GraphQLSchema {
+ assertSchema(schema);
+
+ devAssert(
+ documentAST != null && documentAST.kind === Kind.DOCUMENT,
+ 'Must provide valid Document AST.',
+ );
+
+ if (options?.assumeValid !== true && options?.assumeValidSDL !== true) {
+ assertValidSDLExtension(documentAST, schema);
+ }
+
+ const schemaConfig = schema.toConfig();
+ const extendedConfig = extendSchemaImpl(schemaConfig, documentAST, options);
+ return schemaConfig === extendedConfig
+ ? schema
+ : new GraphQLSchema(extendedConfig);
+}
+
+/**
+ * @internal
+ */
+export function extendSchemaImpl(
+ schemaConfig: GraphQLSchemaNormalizedConfig,
+ documentAST: DocumentNode,
+ options?: Options,
+): GraphQLSchemaNormalizedConfig {
+ // Collect the type definitions and extensions found in the document.
+ const typeDefs: Array<TypeDefinitionNode> = [];
+ const typeExtensionsMap = Object.create(null);
+
+ // New directives and types are separate because a directives and types can
+ // have the same name. For example, a type named "skip".
+ const directiveDefs: Array<DirectiveDefinitionNode> = [];
+
+ let schemaDef: ?SchemaDefinitionNode;
+ // Schema extensions are collected which may add additional operation types.
+ const schemaExtensions: Array<SchemaExtensionNode> = [];
+
+ for (const def of documentAST.definitions) {
+ if (def.kind === Kind.SCHEMA_DEFINITION) {
+ schemaDef = def;
+ } else if (def.kind === Kind.SCHEMA_EXTENSION) {
+ schemaExtensions.push(def);
+ } else if (isTypeDefinitionNode(def)) {
+ typeDefs.push(def);
+ } else if (isTypeExtensionNode(def)) {
+ const extendedTypeName = def.name.value;
+ const existingTypeExtensions = typeExtensionsMap[extendedTypeName];
+ typeExtensionsMap[extendedTypeName] = existingTypeExtensions
+ ? existingTypeExtensions.concat([def])
+ : [def];
+ } else if (def.kind === Kind.DIRECTIVE_DEFINITION) {
+ directiveDefs.push(def);
+ }
+ }
+
+ // If this document contains no new types, extensions, or directives then
+ // return the same unmodified GraphQLSchema instance.
+ if (
+ Object.keys(typeExtensionsMap).length === 0 &&
+ typeDefs.length === 0 &&
+ directiveDefs.length === 0 &&
+ schemaExtensions.length === 0 &&
+ schemaDef == null
+ ) {
+ return schemaConfig;
+ }
+
+ const typeMap = Object.create(null);
+ for (const existingType of schemaConfig.types) {
+ typeMap[existingType.name] = extendNamedType(existingType);
+ }
+
+ for (const typeNode of typeDefs) {
+ const name = typeNode.name.value;
+ typeMap[name] = stdTypeMap[name] ?? buildType(typeNode);
+ }
+
+ const operationTypes = {
+ // Get the extended root operation types.
+ query: schemaConfig.query && replaceNamedType(schemaConfig.query),
+ mutation: schemaConfig.mutation && replaceNamedType(schemaConfig.mutation),
+ subscription:
+ schemaConfig.subscription && replaceNamedType(schemaConfig.subscription),
+ // Then, incorporate schema definition and all schema extensions.
+ ...(schemaDef && getOperationTypes([schemaDef])),
+ ...getOperationTypes(schemaExtensions),
+ };
+
+ // Then produce and return a Schema config with these types.
+ return {
+ description: schemaDef?.description?.value,
+ ...operationTypes,
+ types: objectValues(typeMap),
+ directives: [
+ ...schemaConfig.directives.map(replaceDirective),
+ ...directiveDefs.map(buildDirective),
+ ],
+ extensions: undefined,
+ astNode: schemaDef ?? schemaConfig.astNode,
+ extensionASTNodes: schemaConfig.extensionASTNodes.concat(schemaExtensions),
+ assumeValid: options?.assumeValid ?? false,
+ };
+
+ // Below are functions used for producing this schema that have closed over
+ // this scope and have access to the schema, cache, and newly defined types.
+
+ function replaceType<T: GraphQLType>(type: T): T {
+ if (isListType(type)) {
+ // $FlowFixMe[incompatible-return]
+ return new GraphQLList(replaceType(type.ofType));
+ }
+ if (isNonNullType(type)) {
+ // $FlowFixMe[incompatible-return]
+ return new GraphQLNonNull(replaceType(type.ofType));
+ }
+ return replaceNamedType(type);
+ }
+
+ function replaceNamedType<T: GraphQLNamedType>(type: T): T {
+ // Note: While this could make early assertions to get the correctly
+ // typed values, that would throw immediately while type system
+ // validation with validateSchema() will produce more actionable results.
+ return ((typeMap[type.name]: any): T);
+ }
+
+ function replaceDirective(directive: GraphQLDirective): GraphQLDirective {
+ const config = directive.toConfig();
+ return new GraphQLDirective({
+ ...config,
+ args: mapValue(config.args, extendArg),
+ });
+ }
+
+ function extendNamedType(type: GraphQLNamedType): GraphQLNamedType {
+ if (isIntrospectionType(type) || isSpecifiedScalarType(type)) {
+ // Builtin types are not extended.
+ return type;
+ }
+ if (isScalarType(type)) {
+ return extendScalarType(type);
+ }
+ if (isObjectType(type)) {
+ return extendObjectType(type);
+ }
+ if (isInterfaceType(type)) {
+ return extendInterfaceType(type);
+ }
+ if (isUnionType(type)) {
+ return extendUnionType(type);
+ }
+ if (isEnumType(type)) {
+ return extendEnumType(type);
+ }
+ // istanbul ignore else (See: 'https://github.com/graphql/graphql-js/issues/2618')
+ if (isInputObjectType(type)) {
+ return extendInputObjectType(type);
+ }
+
+ // istanbul ignore next (Not reachable. All possible types have been considered)
+ invariant(false, 'Unexpected type: ' + inspect((type: empty)));
+ }
+
+ function extendInputObjectType(
+ type: GraphQLInputObjectType,
+ ): GraphQLInputObjectType {
+ const config = type.toConfig();
+ const extensions = typeExtensionsMap[config.name] ?? [];
+
+ return new GraphQLInputObjectType({
+ ...config,
+ fields: () => ({
+ ...mapValue(config.fields, (field) => ({
+ ...field,
+ type: replaceType(field.type),
+ })),
+ ...buildInputFieldMap(extensions),
+ }),
+ extensionASTNodes: config.extensionASTNodes.concat(extensions),
+ });
+ }
+
+ function extendEnumType(type: GraphQLEnumType): GraphQLEnumType {
+ const config = type.toConfig();
+ const extensions = typeExtensionsMap[type.name] ?? [];
+
+ return new GraphQLEnumType({
+ ...config,
+ values: {
+ ...config.values,
+ ...buildEnumValueMap(extensions),
+ },
+ extensionASTNodes: config.extensionASTNodes.concat(extensions),
+ });
+ }
+
+ function extendScalarType(type: GraphQLScalarType): GraphQLScalarType {
+ const config = type.toConfig();
+ const extensions = typeExtensionsMap[config.name] ?? [];
+
+ let specifiedByUrl = config.specifiedByUrl;
+ for (const extensionNode of extensions) {
+ specifiedByUrl = getSpecifiedByUrl(extensionNode) ?? specifiedByUrl;
+ }
+
+ return new GraphQLScalarType({
+ ...config,
+ specifiedByUrl,
+ extensionASTNodes: config.extensionASTNodes.concat(extensions),
+ });
+ }
+
+ function extendObjectType(type: GraphQLObjectType): GraphQLObjectType {
+ const config = type.toConfig();
+ const extensions = typeExtensionsMap[config.name] ?? [];
+
+ return new GraphQLObjectType({
+ ...config,
+ interfaces: () => [
+ ...type.getInterfaces().map(replaceNamedType),
+ ...buildInterfaces(extensions),
+ ],
+ fields: () => ({
+ ...mapValue(config.fields, extendField),
+ ...buildFieldMap(extensions),
+ }),
+ extensionASTNodes: config.extensionASTNodes.concat(extensions),
+ });
+ }
+
+ function extendInterfaceType(
+ type: GraphQLInterfaceType,
+ ): GraphQLInterfaceType {
+ const config = type.toConfig();
+ const extensions = typeExtensionsMap[config.name] ?? [];
+
+ return new GraphQLInterfaceType({
+ ...config,
+ interfaces: () => [
+ ...type.getInterfaces().map(replaceNamedType),
+ ...buildInterfaces(extensions),
+ ],
+ fields: () => ({
+ ...mapValue(config.fields, extendField),
+ ...buildFieldMap(extensions),
+ }),
+ extensionASTNodes: config.extensionASTNodes.concat(extensions),
+ });
+ }
+
+ function extendUnionType(type: GraphQLUnionType): GraphQLUnionType {
+ const config = type.toConfig();
+ const extensions = typeExtensionsMap[config.name] ?? [];
+
+ return new GraphQLUnionType({
+ ...config,
+ types: () => [
+ ...type.getTypes().map(replaceNamedType),
+ ...buildUnionTypes(extensions),
+ ],
+ extensionASTNodes: config.extensionASTNodes.concat(extensions),
+ });
+ }
+
+ function extendField(
+ field: GraphQLFieldConfig<mixed, mixed>,
+ ): GraphQLFieldConfig<mixed, mixed> {
+ return {
+ ...field,
+ type: replaceType(field.type),
+ // $FlowFixMe[incompatible-call]
+ args: mapValue(field.args, extendArg),
+ };
+ }
+
+ function extendArg(arg: GraphQLArgumentConfig) {
+ return {
+ ...arg,
+ type: replaceType(arg.type),
+ };
+ }
+
+ function getOperationTypes(
+ nodes: $ReadOnlyArray<SchemaDefinitionNode | SchemaExtensionNode>,
+ ): {|
+ query: ?GraphQLObjectType,
+ mutation: ?GraphQLObjectType,
+ subscription: ?GraphQLObjectType,
+ |} {
+ const opTypes = {};
+ for (const node of nodes) {
+ // istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203')
+ const operationTypesNodes = node.operationTypes ?? [];
+
+ for (const operationType of operationTypesNodes) {
+ opTypes[operationType.operation] = getNamedType(operationType.type);
+ }
+ }
+
+ // Note: While this could make early assertions to get the correctly
+ // typed values below, that would throw immediately while type system
+ // validation with validateSchema() will produce more actionable results.
+ return (opTypes: any);
+ }
+
+ function getNamedType(node: NamedTypeNode): GraphQLNamedType {
+ const name = node.name.value;
+ const type = stdTypeMap[name] ?? typeMap[name];
+
+ if (type === undefined) {
+ throw new Error(`Unknown type: "${name}".`);
+ }
+ return type;
+ }
+
+ function getWrappedType(node: TypeNode): GraphQLType {
+ if (node.kind === Kind.LIST_TYPE) {
+ return new GraphQLList(getWrappedType(node.type));
+ }
+ if (node.kind === Kind.NON_NULL_TYPE) {
+ return new GraphQLNonNull(getWrappedType(node.type));
+ }
+ return getNamedType(node);
+ }
+
+ function buildDirective(node: DirectiveDefinitionNode): GraphQLDirective {
+ const locations = node.locations.map(
+ ({ value }) => ((value: any): DirectiveLocationEnum),
+ );
+
+ return new GraphQLDirective({
+ name: node.name.value,
+ description: getDescription(node, options),
+ locations,
+ isRepeatable: node.repeatable,
+ args: buildArgumentMap(node.arguments),
+ astNode: node,
+ });
+ }
+
+ function buildFieldMap(
+ nodes: $ReadOnlyArray<
+ | InterfaceTypeDefinitionNode
+ | InterfaceTypeExtensionNode
+ | ObjectTypeDefinitionNode
+ | ObjectTypeExtensionNode,
+ >,
+ ): GraphQLFieldConfigMap<mixed, mixed> {
+ const fieldConfigMap = Object.create(null);
+ for (const node of nodes) {
+ // istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203')
+ const nodeFields = node.fields ?? [];
+
+ for (const field of nodeFields) {
+ fieldConfigMap[field.name.value] = {
+ // Note: While this could make assertions to get the correctly typed
+ // value, that would throw immediately while type system validation
+ // with validateSchema() will produce more actionable results.
+ type: (getWrappedType(field.type): any),
+ description: getDescription(field, options),
+ args: buildArgumentMap(field.arguments),
+ deprecationReason: getDeprecationReason(field),
+ astNode: field,
+ };
+ }
+ }
+ return fieldConfigMap;
+ }
+
+ function buildArgumentMap(
+ args: ?$ReadOnlyArray<InputValueDefinitionNode>,
+ ): GraphQLFieldConfigArgumentMap {
+ // istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203')
+ const argsNodes = args ?? [];
+
+ const argConfigMap = Object.create(null);
+ for (const arg of argsNodes) {
+ // Note: While this could make assertions to get the correctly typed
+ // value, that would throw immediately while type system validation
+ // with validateSchema() will produce more actionable results.
+ const type: any = getWrappedType(arg.type);
+
+ argConfigMap[arg.name.value] = {
+ type,
+ description: getDescription(arg, options),
+ defaultValue: valueFromAST(arg.defaultValue, type),
+ deprecationReason: getDeprecationReason(arg),
+ astNode: arg,
+ };
+ }
+ return argConfigMap;
+ }
+
+ function buildInputFieldMap(
+ nodes: $ReadOnlyArray<
+ InputObjectTypeDefinitionNode | InputObjectTypeExtensionNode,
+ >,
+ ): GraphQLInputFieldConfigMap {
+ const inputFieldMap = Object.create(null);
+ for (const node of nodes) {
+ // istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203')
+ const fieldsNodes = node.fields ?? [];
+
+ for (const field of fieldsNodes) {
+ // Note: While this could make assertions to get the correctly typed
+ // value, that would throw immediately while type system validation
+ // with validateSchema() will produce more actionable results.
+ const type: any = getWrappedType(field.type);
+
+ inputFieldMap[field.name.value] = {
+ type,
+ description: getDescription(field, options),
+ defaultValue: valueFromAST(field.defaultValue, type),
+ deprecationReason: getDeprecationReason(field),
+ astNode: field,
+ };
+ }
+ }
+ return inputFieldMap;
+ }
+
+ function buildEnumValueMap(
+ nodes: $ReadOnlyArray<EnumTypeDefinitionNode | EnumTypeExtensionNode>,
+ ): GraphQLEnumValueConfigMap {
+ const enumValueMap = Object.create(null);
+ for (const node of nodes) {
+ // istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203')
+ const valuesNodes = node.values ?? [];
+
+ for (const value of valuesNodes) {
+ enumValueMap[value.name.value] = {
+ description: getDescription(value, options),
+ deprecationReason: getDeprecationReason(value),
+ astNode: value,
+ };
+ }
+ }
+ return enumValueMap;
+ }
+
+ function buildInterfaces(
+ nodes: $ReadOnlyArray<
+ | InterfaceTypeDefinitionNode
+ | InterfaceTypeExtensionNode
+ | ObjectTypeDefinitionNode
+ | ObjectTypeExtensionNode,
+ >,
+ ): Array<GraphQLInterfaceType> {
+ const interfaces = [];
+ for (const node of nodes) {
+ // istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203')
+ const interfacesNodes = node.interfaces ?? [];
+
+ for (const type of interfacesNodes) {
+ // Note: While this could make assertions to get the correctly typed
+ // values below, that would throw immediately while type system
+ // validation with validateSchema() will produce more actionable
+ // results.
+ interfaces.push((getNamedType(type): any));
+ }
+ }
+ return interfaces;
+ }
+
+ function buildUnionTypes(
+ nodes: $ReadOnlyArray<UnionTypeDefinitionNode | UnionTypeExtensionNode>,
+ ): Array<GraphQLObjectType> {
+ const types = [];
+ for (const node of nodes) {
+ // istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203')
+ const typeNodes = node.types ?? [];
+
+ for (const type of typeNodes) {
+ // Note: While this could make assertions to get the correctly typed
+ // values below, that would throw immediately while type system
+ // validation with validateSchema() will produce more actionable
+ // results.
+ types.push((getNamedType(type): any));
+ }
+ }
+ return types;
+ }
+
+ function buildType(astNode: TypeDefinitionNode): GraphQLNamedType {
+ const name = astNode.name.value;
+ const description = getDescription(astNode, options);
+ const extensionNodes = typeExtensionsMap[name] ?? [];
+
+ switch (astNode.kind) {
+ case Kind.OBJECT_TYPE_DEFINITION: {
+ const extensionASTNodes = (extensionNodes: any);
+ const allNodes = [astNode, ...extensionASTNodes];
+
+ return new GraphQLObjectType({
+ name,
+ description,
+ interfaces: () => buildInterfaces(allNodes),
+ fields: () => buildFieldMap(allNodes),
+ astNode,
+ extensionASTNodes,
+ });
+ }
+ case Kind.INTERFACE_TYPE_DEFINITION: {
+ const extensionASTNodes = (extensionNodes: any);
+ const allNodes = [astNode, ...extensionASTNodes];
+
+ return new GraphQLInterfaceType({
+ name,
+ description,
+ interfaces: () => buildInterfaces(allNodes),
+ fields: () => buildFieldMap(allNodes),
+ astNode,
+ extensionASTNodes,
+ });
+ }
+ case Kind.ENUM_TYPE_DEFINITION: {
+ const extensionASTNodes = (extensionNodes: any);
+ const allNodes = [astNode, ...extensionASTNodes];
+
+ return new GraphQLEnumType({
+ name,
+ description,
+ values: buildEnumValueMap(allNodes),
+ astNode,
+ extensionASTNodes,
+ });
+ }
+ case Kind.UNION_TYPE_DEFINITION: {
+ const extensionASTNodes = (extensionNodes: any);
+ const allNodes = [astNode, ...extensionASTNodes];
+
+ return new GraphQLUnionType({
+ name,
+ description,
+ types: () => buildUnionTypes(allNodes),
+ astNode,
+ extensionASTNodes,
+ });
+ }
+ case Kind.SCALAR_TYPE_DEFINITION: {
+ const extensionASTNodes = (extensionNodes: any);
+
+ return new GraphQLScalarType({
+ name,
+ description,
+ specifiedByUrl: getSpecifiedByUrl(astNode),
+ astNode,
+ extensionASTNodes,
+ });
+ }
+ case Kind.INPUT_OBJECT_TYPE_DEFINITION: {
+ const extensionASTNodes = (extensionNodes: any);
+ const allNodes = [astNode, ...extensionASTNodes];
+
+ return new GraphQLInputObjectType({
+ name,
+ description,
+ fields: () => buildInputFieldMap(allNodes),
+ astNode,
+ extensionASTNodes,
+ });
+ }
+ }
+
+ // istanbul ignore next (Not reachable. All possible type definition nodes have been considered)
+ invariant(
+ false,
+ 'Unexpected type definition node: ' + inspect((astNode: empty)),
+ );
+ }
+}
+
+const stdTypeMap = keyMap(
+ specifiedScalarTypes.concat(introspectionTypes),
+ (type) => type.name,
+);
+
+/**
+ * Given a field or enum value node, returns the string value for the
+ * deprecation reason.
+ */
+function getDeprecationReason(
+ node:
+ | EnumValueDefinitionNode
+ | FieldDefinitionNode
+ | InputValueDefinitionNode,
+): ?string {
+ const deprecated = getDirectiveValues(GraphQLDeprecatedDirective, node);
+ return (deprecated?.reason: any);
+}
+
+/**
+ * Given a scalar node, returns the string value for the specifiedByUrl.
+ */
+function getSpecifiedByUrl(
+ node: ScalarTypeDefinitionNode | ScalarTypeExtensionNode,
+): ?string {
+ const specifiedBy = getDirectiveValues(GraphQLSpecifiedByDirective, node);
+ return (specifiedBy?.url: any);
+}
+
+/**
+ * Given an ast node, returns its string description.
+ * @deprecated: provided to ease adoption and will be removed in v16.
+ *
+ * Accepts options as a second argument:
+ *
+ * - commentDescriptions:
+ * Provide true to use preceding comments as the description.
+ *
+ */
+export function getDescription(
+ node: { +description?: StringValueNode, +loc?: Location, ... },
+ options: ?{ commentDescriptions?: boolean, ... },
+): void | string {
+ if (node.description) {
+ return node.description.value;
+ }
+ if (options?.commentDescriptions === true) {
+ const rawValue = getLeadingCommentBlock(node);
+ if (rawValue !== undefined) {
+ return dedentBlockStringValue('\n' + rawValue);
+ }
+ }
+}
+
+function getLeadingCommentBlock(node): void | string {
+ const loc = node.loc;
+ if (!loc) {
+ return;
+ }
+ const comments = [];
+ let token = loc.startToken.prev;
+ while (
+ token != null &&
+ token.kind === TokenKind.COMMENT &&
+ token.next &&
+ token.prev &&
+ token.line + 1 === token.next.line &&
+ token.line !== token.prev.line
+ ) {
+ const value = String(token.value);
+ comments.push(value);
+ token = token.prev;
+ }
+ return comments.length > 0 ? comments.reverse().join('\n') : undefined;
+}