apollo-codegen-core
Version:
Core generator APIs for Apollo Codegen
202 lines (186 loc) • 5.19 kB
text/typescript
import {
visit,
Kind,
isEqualType,
isAbstractType,
SchemaMetaFieldDef,
TypeMetaFieldDef,
TypeNameMetaFieldDef,
GraphQLCompositeType,
GraphQLEnumValue,
GraphQLError,
GraphQLSchema,
GraphQLType,
ASTNode,
Location,
ValueNode,
OperationDefinitionNode,
SelectionSetNode,
FieldNode,
GraphQLField,
DocumentNode,
DirectiveNode,
isListType,
isNonNullType,
isObjectType,
isInterfaceType,
isUnionType,
} from "graphql";
declare module "graphql/utilities/buildASTSchema" {
function buildASTSchema(
ast: DocumentNode,
options?: { assumeValid?: boolean; commentDescriptions?: boolean }
): GraphQLSchema;
}
export function sortEnumValues(values: GraphQLEnumValue[]): GraphQLEnumValue[] {
return values.sort((a, b) =>
a.value < b.value ? -1 : a.value > b.value ? 1 : 0
);
}
export function isList(type: GraphQLType): boolean {
return isListType(type) || (isNonNullType(type) && isListType(type.ofType));
}
export function isMetaFieldName(name: string) {
return name.startsWith("__");
}
export function removeConnectionDirectives(ast: ASTNode) {
return visit(ast, {
Directive(node: DirectiveNode): DirectiveNode | null {
if (node.name.value === "connection") return null;
return node;
},
});
}
export function removeClientDirectives(ast: ASTNode) {
return visit(ast, {
Field(node: FieldNode): FieldNode | null {
if (
node.directives &&
node.directives.find((directive) => directive.name.value === "client")
)
return null;
return node;
},
OperationDefinition: {
leave(node: OperationDefinitionNode): OperationDefinitionNode | null {
if (!node.selectionSet.selections.length) return null;
return node;
},
},
});
}
const typenameField = {
kind: Kind.FIELD,
name: { kind: Kind.NAME, value: "__typename" },
};
export function sourceAt(location: Location) {
return location.source.body.slice(location.start, location.end);
}
export function filePathForNode(node: ASTNode): string {
const name = node.loc && node.loc.source && node.loc.source.name;
if (!name || name === "GraphQL") {
throw new Error("Node does not seem to have a file path");
}
return name;
}
export function valueFromValueNode(
valueNode: ValueNode
): any | { kind: "Variable"; variableName: string } {
switch (valueNode.kind) {
case "IntValue":
case "FloatValue":
return Number(valueNode.value);
case "NullValue":
return null;
case "ListValue":
return valueNode.values.map(valueFromValueNode);
case "ObjectValue":
return valueNode.fields.reduce((object, field) => {
object[field.name.value] = valueFromValueNode(field.value);
return object;
}, {} as any);
case "Variable":
return { kind: "Variable", variableName: valueNode.name.value };
default:
return valueNode.value;
}
}
export function isTypeProperSuperTypeOf(
schema: GraphQLSchema,
maybeSuperType: GraphQLCompositeType,
subType: GraphQLCompositeType
) {
return (
isEqualType(maybeSuperType, subType) ||
(isObjectType(subType) &&
isAbstractType(maybeSuperType) &&
schema.isPossibleType(maybeSuperType, subType))
);
}
// Utility functions extracted from graphql-js
/**
* Extracts the root type of the operation from the schema.
*/
export function getOperationRootType(
schema: GraphQLSchema,
operation: OperationDefinitionNode
) {
switch (operation.operation) {
case "query":
return schema.getQueryType();
case "mutation":
const mutationType = schema.getMutationType();
if (!mutationType) {
throw new GraphQLError("Schema is not configured for mutations", [
operation,
]);
}
return mutationType;
case "subscription":
const subscriptionType = schema.getSubscriptionType();
if (!subscriptionType) {
throw new GraphQLError("Schema is not configured for subscriptions", [
operation,
]);
}
return subscriptionType;
default:
throw new GraphQLError(
"Can only compile queries, mutations and subscriptions",
[operation]
);
}
}
/**
* Not exactly the same as the executor's definition of getFieldDef, in this
* statically evaluated environment we do not always have an Object type,
* and need to handle Interface and Union types.
*/
export function getFieldDef(
schema: GraphQLSchema,
parentType: GraphQLCompositeType,
fieldAST: FieldNode
): GraphQLField<any, any> | undefined {
const name = fieldAST.name.value;
if (
name === SchemaMetaFieldDef.name &&
schema.getQueryType() === parentType
) {
return SchemaMetaFieldDef;
}
if (name === TypeMetaFieldDef.name && schema.getQueryType() === parentType) {
return TypeMetaFieldDef;
}
if (
name === TypeNameMetaFieldDef.name &&
(isObjectType(parentType) ||
isInterfaceType(parentType) ||
isUnionType(parentType))
) {
return TypeNameMetaFieldDef;
}
if (isObjectType(parentType) || isInterfaceType(parentType)) {
return parentType.getFields()[name];
}
return undefined;
}