All Downloads are FREE. Search and download functionalities are using the official Maven repository.

node_modules.apollo-codegen.lib.compilation.js Maven / Gradle / Ivy

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




© 2015 - 2024 Weber Informatics LLC | Privacy Policy