swc/crates/swc_bundler/tests/.cache/deno/783f5890dde13e85ec3009ba3cc8b211e459d635.ts

582 lines
19 KiB
TypeScript
Raw Normal View History

// Loaded from https://deno.land/x/graphql_deno@v15.0.0/lib/utilities/extendSchema.js
import objectValues from '../polyfills/objectValues.js';
import keyMap from '../jsutils/keyMap.js';
import inspect from '../jsutils/inspect.js';
import mapValue from '../jsutils/mapValue.js';
import invariant from '../jsutils/invariant.js';
import devAssert from '../jsutils/devAssert.js';
import { Kind } from '../language/kinds.js';
import { TokenKind } from '../language/tokenKind.js';
import { dedentBlockStringValue } from '../language/blockString.js';
import { isTypeDefinitionNode, isTypeExtensionNode } from '../language/predicates.js';
import { assertValidSDLExtension } from '../validation/validate.js';
import { getDirectiveValues } from '../execution/values.js';
import { specifiedScalarTypes, isSpecifiedScalarType } from '../type/scalars.js';
import { introspectionTypes, isIntrospectionType } from '../type/introspection.js';
import { GraphQLDirective, GraphQLDeprecatedDirective } from '../type/directives.js';
import { assertSchema, GraphQLSchema } from '../type/schema.js';
import { isScalarType, isObjectType, isInterfaceType, isUnionType, isListType, isNonNullType, isEnumType, isInputObjectType, GraphQLList, GraphQLNonNull, GraphQLScalarType, GraphQLObjectType, GraphQLInterfaceType, GraphQLUnionType, GraphQLEnumType, GraphQLInputObjectType } from '../type/definition.js';
import { valueFromAST } from './valueFromAST.js';
/**
* 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, documentAST, options) {
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, documentAST, options) {
// Collect the type definitions and extensions found in the document.
const typeDefs = [];
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 = [];
let schemaDef; // Schema extensions are collected which may add additional operation types.
const schemaExtensions = [];
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(type) {
if (isListType(type)) {
return new GraphQLList(replaceType(type.ofType));
} else if (isNonNullType(type)) {
return new GraphQLNonNull(replaceType(type.ofType));
}
return replaceNamedType(type);
}
function replaceNamedType(type) {
// 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];
}
function replaceDirective(directive) {
const config = directive.toConfig();
return new GraphQLDirective({ ...config,
args: mapValue(config.args, extendArg)
});
}
function extendNamedType(type) {
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);
}
if (isInputObjectType(type)) {
return extendInputObjectType(type);
} // Not reachable. All possible types have been considered.
invariant(false, 'Unexpected type: ' + inspect(type));
}
function extendInputObjectType(type) {
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) {
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) {
const config = type.toConfig();
const extensions = typeExtensionsMap[config.name] ?? [];
return new GraphQLScalarType({ ...config,
extensionASTNodes: config.extensionASTNodes.concat(extensions)
});
}
function extendObjectType(type) {
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) {
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) {
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) {
return { ...field,
type: replaceType(field.type),
args: mapValue(field.args, extendArg)
};
}
function extendArg(arg) {
return { ...arg,
type: replaceType(arg.type)
};
}
function getOperationTypes(nodes) {
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;
}
function getNamedType(node) {
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) {
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) {
const locations = node.locations.map(({
value
}) => value);
return new GraphQLDirective({
name: node.name.value,
description: getDescription(node, options),
locations,
isRepeatable: node.repeatable,
args: buildArgumentMap(node.arguments),
astNode: node
});
}
function buildFieldMap(nodes) {
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),
description: getDescription(field, options),
args: buildArgumentMap(field.arguments),
deprecationReason: getDeprecationReason(field),
astNode: field
};
}
}
return fieldConfigMap;
}
function buildArgumentMap(args) {
/* 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 = getWrappedType(arg.type);
argConfigMap[arg.name.value] = {
type,
description: getDescription(arg, options),
defaultValue: valueFromAST(arg.defaultValue, type),
astNode: arg
};
}
return argConfigMap;
}
function buildInputFieldMap(nodes) {
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 = getWrappedType(field.type);
inputFieldMap[field.name.value] = {
type,
description: getDescription(field, options),
defaultValue: valueFromAST(field.defaultValue, type),
astNode: field
};
}
}
return inputFieldMap;
}
function buildEnumValueMap(nodes) {
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) {
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));
}
}
return interfaces;
}
function buildUnionTypes(nodes) {
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));
}
}
return types;
}
function buildType(astNode) {
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;
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;
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;
const allNodes = [astNode, ...extensionASTNodes];
return new GraphQLEnumType({
name,
description,
values: buildEnumValueMap(allNodes),
astNode,
extensionASTNodes
});
}
case Kind.UNION_TYPE_DEFINITION:
{
const extensionASTNodes = extensionNodes;
const allNodes = [astNode, ...extensionASTNodes];
return new GraphQLUnionType({
name,
description,
types: () => buildUnionTypes(allNodes),
astNode,
extensionASTNodes
});
}
case Kind.SCALAR_TYPE_DEFINITION:
{
const extensionASTNodes = extensionNodes;
return new GraphQLScalarType({
name,
description,
astNode,
extensionASTNodes
});
}
case Kind.INPUT_OBJECT_TYPE_DEFINITION:
{
const extensionASTNodes = extensionNodes;
const allNodes = [astNode, ...extensionASTNodes];
return new GraphQLInputObjectType({
name,
description,
fields: () => buildInputFieldMap(allNodes),
astNode,
extensionASTNodes
});
}
} // Not reachable. All possible type definition nodes have been considered.
invariant(false, 'Unexpected type definition node: ' + inspect(astNode));
}
}
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) {
const deprecated = getDirectiveValues(GraphQLDeprecatedDirective, node);
return deprecated?.reason;
}
/**
* 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, options) {
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) {
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;
}