node_modules.apollo-codegen.lib.compilation.js Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of apollo-client-maven-plugin Show documentation
Show all versions of apollo-client-maven-plugin Show documentation
Maven plugin for generating graphql clients
The newest version!
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const graphql_1 = require("graphql");
const graphql_2 = require("./utilities/graphql");
const crypto_1 = require("crypto");
function compileToIR(schema, document, options = { mergeInFieldsFromFragmentSpreads: true }) {
if (options.addTypename) {
document = graphql_2.withTypenameFieldAddedWhereNeeded(schema, document);
}
const compiler = new Compiler(schema, document, options);
const operations = Object.create(null);
compiler.compiledOperations.forEach(operation => {
operations[operation.operationName] = operation;
});
const fragments = Object.create(null);
for (const [fragmentName, compiledFragment] of compiler.compiledFragmentMap.entries()) {
fragments[fragmentName] = compiledFragment;
}
const typesUsed = compiler.typesUsed;
return { schema, operations, fragments, typesUsed, options };
}
exports.compileToIR = compileToIR;
class Compiler {
constructor(schema, document, options) {
this.schema = schema;
this.options = options;
this.typesUsedSet = new Set();
this.fragmentMap = new Map();
const operations = [];
for (const definition of document.definitions) {
switch (definition.kind) {
case graphql_1.Kind.OPERATION_DEFINITION:
operations.push(definition);
break;
case graphql_1.Kind.FRAGMENT_DEFINITION:
this.fragmentMap.set(definition.name.value, definition);
break;
}
}
this.compiledFragmentMap = new Map();
this.compiledOperations = operations.map(this.compileOperation, this);
for (const fragmentName of this.fragmentMap.keys()) {
this.compiledFragmentNamed(fragmentName);
}
}
addTypeUsed(type) {
if (this.typesUsedSet.has(type))
return;
if (type instanceof graphql_1.GraphQLEnumType ||
type instanceof graphql_1.GraphQLInputObjectType ||
(type instanceof graphql_1.GraphQLScalarType && !graphql_2.isBuiltInScalarType(type))) {
this.typesUsedSet.add(type);
}
if (type instanceof graphql_1.GraphQLInputObjectType) {
for (const field of Object.values(type.getFields())) {
this.addTypeUsed(graphql_1.getNamedType(field.type));
}
}
}
get typesUsed() {
return Array.from(this.typesUsedSet);
}
compileOperation(operationDefinition) {
if (!operationDefinition.name) {
throw new Error('Operations should be named');
}
const filePath = graphql_2.filePathForNode(operationDefinition);
const operationName = operationDefinition.name.value;
const operationType = operationDefinition.operation;
const variables = (operationDefinition.variableDefinitions || []).map(node => {
const name = node.variable.name.value;
const type = graphql_1.typeFromAST(this.schema, node.type);
this.addTypeUsed(graphql_1.getNamedType(type));
return { name, type };
});
const source = graphql_1.print(operationDefinition);
const rootType = graphql_2.getOperationRootType(this.schema, operationDefinition);
const groupedVisitedFragmentSet = new Map();
const groupedFieldSet = this.collectFields(rootType, operationDefinition.selectionSet, undefined, groupedVisitedFragmentSet);
const fragmentsReferencedSet = new Set();
const { fields } = this.resolveFields(rootType, groupedFieldSet, groupedVisitedFragmentSet, fragmentsReferencedSet);
const fragmentsReferenced = Array.from(fragmentsReferencedSet.keys());
const sourceWithFragments = [
source,
...fragmentsReferenced.map(fragmentName => {
return this.compiledFragmentNamed(fragmentName).source;
})
].join('\n');
const hash = crypto_1.createHash('sha256');
hash.update(sourceWithFragments);
const operationId = hash.digest('hex');
return {
filePath,
operationName,
operationType,
rootType,
variables,
source,
fields,
fragmentsReferenced,
sourceWithFragments,
operationId
};
}
fragmentNamed(fragmentName) {
const fragment = this.fragmentMap.get(fragmentName);
if (!fragment) {
throw new graphql_1.GraphQLError(`Cannot find fragment "${fragmentName}"`);
}
return fragment;
}
compiledFragmentNamed(fragmentName) {
let compiledFragment = this.compiledFragmentMap.get(fragmentName);
if (compiledFragment)
return compiledFragment;
const fragment = this.fragmentNamed(fragmentName);
const filePath = graphql_2.filePathForNode(fragment);
const source = graphql_1.print(fragment);
const typeCondition = graphql_1.typeFromAST(this.schema, fragment.typeCondition);
const possibleTypes = this.possibleTypesForType(typeCondition);
const groupedVisitedFragmentSet = new Map();
const groupedFieldSet = this.collectFields(typeCondition, fragment.selectionSet, undefined, groupedVisitedFragmentSet);
const fragmentsReferencedSet = new Set();
const { fields, fragmentSpreads, inlineFragments } = this.resolveFields(typeCondition, groupedFieldSet, groupedVisitedFragmentSet, fragmentsReferencedSet);
const fragmentsReferenced = Array.from(fragmentsReferencedSet.keys());
compiledFragment = {
filePath,
fragmentName,
source,
typeCondition,
possibleTypes,
fields,
fragmentSpreads,
inlineFragments,
fragmentsReferenced
};
this.compiledFragmentMap.set(fragmentName, compiledFragment);
return compiledFragment;
}
collectFields(parentType, selectionSet, groupedFieldSet = new Map(), groupedVisitedFragmentSet = new Map()) {
if (!graphql_1.isCompositeType(parentType)) {
throw new Error(`parentType should be a composite type, but is "${String(parentType)}"`);
}
for (const selection of selectionSet.selections) {
switch (selection.kind) {
case graphql_1.Kind.FIELD: {
const fieldName = selection.name.value;
const responseName = selection.alias ? selection.alias.value : fieldName;
const field = graphql_2.getFieldDef(this.schema, parentType, selection);
if (!field) {
throw new graphql_1.GraphQLError(`Cannot query field "${fieldName}" on type "${String(parentType)}"`, [
selection
]);
}
let fieldSet = groupedFieldSet.get(responseName);
if (!fieldSet) {
fieldSet = [];
groupedFieldSet.set(responseName, fieldSet);
}
fieldSet.push([
parentType,
{
responseName,
fieldName,
args: selection.arguments ? argumentsFromAST(selection.arguments) : undefined,
type: field.type,
directives: selection.directives,
selectionSet: selection.selectionSet
}
]);
break;
}
case graphql_1.Kind.INLINE_FRAGMENT: {
const typeCondition = selection.typeCondition;
const inlineFragmentType = typeCondition
? graphql_1.typeFromAST(this.schema, typeCondition)
: parentType;
if (!graphql_1.doTypesOverlap(this.schema, inlineFragmentType, parentType))
continue;
const effectiveType = parentType instanceof graphql_1.GraphQLObjectType ? parentType : inlineFragmentType;
this.collectFields(effectiveType, selection.selectionSet, groupedFieldSet, groupedVisitedFragmentSet);
break;
}
case graphql_1.Kind.FRAGMENT_SPREAD: {
const fragmentName = selection.name.value;
const fragment = this.fragmentNamed(fragmentName);
const typeCondition = fragment.typeCondition;
const fragmentType = graphql_1.typeFromAST(this.schema, typeCondition);
let visitedFragmentSet = groupedVisitedFragmentSet.get(parentType);
if (!visitedFragmentSet) {
visitedFragmentSet = new Set();
groupedVisitedFragmentSet.set(parentType, visitedFragmentSet);
}
if (visitedFragmentSet.has(fragmentName))
continue;
visitedFragmentSet.add(fragmentName);
if (!graphql_1.doTypesOverlap(this.schema, fragmentType, parentType))
continue;
const effectiveType = parentType instanceof graphql_1.GraphQLObjectType ? parentType : fragmentType;
this.collectFields(effectiveType, fragment.selectionSet, this.options.mergeInFieldsFromFragmentSpreads ? groupedFieldSet : undefined, groupedVisitedFragmentSet);
break;
}
}
}
return groupedFieldSet;
}
possibleTypesForType(type) {
if (graphql_1.isAbstractType(type)) {
return this.schema.getPossibleTypes(type);
}
else {
return [type];
}
}
mergeSelectionSets(parentType, fieldSet, groupedVisitedFragmentSet) {
const groupedFieldSet = new Map();
for (const [, field] of fieldSet) {
const selectionSet = field.selectionSet;
if (selectionSet) {
this.collectFields(parentType, selectionSet, groupedFieldSet, groupedVisitedFragmentSet);
}
}
return groupedFieldSet;
}
resolveFields(parentType, groupedFieldSet, groupedVisitedFragmentSet, fragmentsReferencedSet) {
const fields = [];
for (let [responseName, fieldSet] of groupedFieldSet.entries()) {
fieldSet = fieldSet.filter(([typeCondition]) => graphql_1.isTypeSubTypeOf(this.schema, parentType, typeCondition));
if (fieldSet.length < 1)
continue;
const [, firstField] = fieldSet[0];
const fieldName = firstField.fieldName;
const args = firstField.args;
const type = firstField.type;
let field = { responseName, fieldName, type };
if (args && args.length > 0) {
field.args = args;
}
const isConditional = fieldSet.some(([, field]) => {
return (!!field.directives &&
field.directives.some(directive => {
const directiveName = directive.name.value;
return directiveName == 'skip' || directiveName == 'include';
}));
});
if (isConditional) {
field.isConditional = true;
}
if (parentType instanceof graphql_1.GraphQLObjectType || parentType instanceof graphql_1.GraphQLInterfaceType) {
const fieldDef = parentType.getFields()[fieldName];
if (fieldDef) {
const description = fieldDef.description;
if (description) {
field.description = description;
}
field.isDeprecated = fieldDef.isDeprecated;
field.deprecationReason = fieldDef.deprecationReason;
}
}
const bareType = graphql_1.getNamedType(type);
this.addTypeUsed(bareType);
if (graphql_1.isCompositeType(bareType)) {
const subSelectionGroupedVisitedFragmentSet = new Map();
const subSelectionGroupedFieldSet = this.mergeSelectionSets(bareType, fieldSet, subSelectionGroupedVisitedFragmentSet);
const { fields, fragmentSpreads, inlineFragments } = this.resolveFields(bareType, subSelectionGroupedFieldSet, subSelectionGroupedVisitedFragmentSet, fragmentsReferencedSet);
field = Object.assign({}, field, { fields, fragmentSpreads, inlineFragments });
}
fields.push(field);
}
const fragmentSpreads = this.fragmentSpreadsForParentType(parentType, groupedVisitedFragmentSet);
const inlineFragments = this.resolveInlineFragments(parentType, groupedFieldSet, groupedVisitedFragmentSet, fragmentsReferencedSet);
if (fragmentsReferencedSet) {
for (const visitedFragmentSet of groupedVisitedFragmentSet.values()) {
for (const visitedFragment of visitedFragmentSet) {
fragmentsReferencedSet.add(visitedFragment);
}
}
for (let fragmentName of fragmentSpreads) {
const compiledFragment = this.compiledFragmentNamed(fragmentName);
for (let fragmentReferenced of compiledFragment.fragmentsReferenced) {
fragmentsReferencedSet.add(fragmentReferenced);
}
}
}
return { fields, fragmentSpreads, inlineFragments };
}
resolveInlineFragments(parentType, groupedFieldSet, groupedVisitedFragmentSet, fragmentsReferencedSet) {
return this.collectPossibleTypes(parentType, groupedFieldSet, groupedVisitedFragmentSet).map(typeCondition => {
const { fields, fragmentSpreads } = this.resolveFields(typeCondition, groupedFieldSet, groupedVisitedFragmentSet, fragmentsReferencedSet);
const possibleTypes = this.possibleTypesForType(typeCondition);
return { typeCondition, possibleTypes, fields, fragmentSpreads };
});
}
collectPossibleTypes(parentType, groupedFieldSet, groupedVisitedFragmentSet) {
if (!graphql_1.isAbstractType(parentType))
return [];
const possibleTypes = new Set();
for (const fieldSet of groupedFieldSet.values()) {
for (const [typeCondition] of fieldSet) {
if (typeCondition instanceof graphql_1.GraphQLObjectType &&
this.schema.isPossibleType(parentType, typeCondition)) {
possibleTypes.add(typeCondition);
}
}
}
if (groupedVisitedFragmentSet) {
for (const effectiveType of groupedVisitedFragmentSet.keys()) {
if (effectiveType instanceof graphql_1.GraphQLObjectType &&
this.schema.isPossibleType(parentType, effectiveType)) {
possibleTypes.add(effectiveType);
}
}
}
return Array.from(possibleTypes);
}
fragmentSpreadsForParentType(parentType, groupedVisitedFragmentSet) {
if (!groupedVisitedFragmentSet)
return [];
let fragmentSpreads = new Set();
for (const [effectiveType, visitedFragmentSet] of groupedVisitedFragmentSet) {
if (!graphql_2.isTypeProperSuperTypeOf(this.schema, effectiveType, parentType))
continue;
for (const fragmentName of visitedFragmentSet.keys()) {
fragmentSpreads.add(fragmentName);
}
}
return Array.from(fragmentSpreads);
}
}
function argumentsFromAST(args) {
return (args &&
args.map(arg => {
return { name: arg.name.value, value: graphql_2.valueFromValueNode(arg.value) };
}));
}
//# sourceMappingURL=compilation.js.map