flow like the river
This commit is contained in:
commit
013fe673f3
42435 changed files with 5764238 additions and 0 deletions
782
BACK_BACK/node_modules/graphql/utilities/extendSchema.js.flow
generated
vendored
Executable file
782
BACK_BACK/node_modules/graphql/utilities/extendSchema.js.flow
generated
vendored
Executable file
|
|
@ -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;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue