UNPKG

@graphql-tools/executor

Version:

Fork of GraphQL.js' execute function

190 lines (189 loc) • 7.84 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.collectIncrementalFields = collectIncrementalFields; exports.collectIncrementalSubfields = collectIncrementalSubfields; exports.buildExecutionPlan = buildExecutionPlan; exports.groupedFieldSetToFieldMap = groupedFieldSetToFieldMap; exports.rootDeferUsage = rootDeferUsage; exports.labelForDeferUsageSet = labelForDeferUsageSet; const graphql_1 = require("graphql"); const utils_1 = require("@graphql-tools/utils"); const getBySet_js_1 = require("./getBySet.js"); const isSameSet_js_1 = require("./isSameSet.js"); class FieldDetailsMap extends Map { add(key, item) { const group = this.get(key); if (group === undefined) { this.set(key, [item]); } else { group.push(item); } } } function collectIncrementalFields(schema, fragments, variableValues, runtimeType, selectionSet, parentDeferUsage) { const groupedFieldSet = new FieldDetailsMap(); const newDeferUsages = []; const context = { schema, fragments, variableValues, runtimeType, visitedFragmentNames: new Map(), }; collectFieldsImpl(context, selectionSet, groupedFieldSet, newDeferUsages, parentDeferUsage); return { groupedFieldSet, newDeferUsages }; } function collectIncrementalSubfields(schema, fragments, variableValues, returnType, fieldNodes, parentDeferUsage, deferUsageByFieldNode) { const groupedFieldSet = new FieldDetailsMap(); const newDeferUsages = []; const context = { schema, fragments, variableValues, runtimeType: returnType, visitedFragmentNames: new Map(), }; for (const node of fieldNodes) { if (node.selectionSet) { const deferUsage = deferUsageByFieldNode?.has(node) ? deferUsageByFieldNode.get(node) : parentDeferUsage; collectFieldsImpl(context, node.selectionSet, groupedFieldSet, newDeferUsages, deferUsage); } } return { groupedFieldSet, newDeferUsages }; } function collectFieldsImpl(context, selectionSet, groupedFieldSet, newDeferUsages, deferUsage) { const { schema, fragments, variableValues, runtimeType, visitedFragmentNames } = context; for (const selection of selectionSet.selections) { switch (selection.kind) { case graphql_1.Kind.FIELD: { if (!(0, utils_1.shouldIncludeNode)(variableValues, selection)) { continue; } groupedFieldSet.add((0, utils_1.getFieldEntryKey)(selection), { node: selection, deferUsage, }); break; } case graphql_1.Kind.INLINE_FRAGMENT: { if (!(0, utils_1.shouldIncludeNode)(variableValues, selection) || !(0, utils_1.doesFragmentConditionMatch)(schema, selection, runtimeType)) { continue; } const newDeferUsage = getDeferUsage(variableValues, selection, deferUsage); if (!newDeferUsage) { collectFieldsImpl(context, selection.selectionSet, groupedFieldSet, newDeferUsages, deferUsage); } else { newDeferUsages.push(newDeferUsage); collectFieldsImpl(context, selection.selectionSet, groupedFieldSet, newDeferUsages, newDeferUsage); } break; } case graphql_1.Kind.FRAGMENT_SPREAD: { const fragName = selection.name.value; if (!(0, utils_1.shouldIncludeNode)(variableValues, selection)) { continue; } const fragment = fragments[fragName]; if (!fragment || !(0, utils_1.doesFragmentConditionMatch)(schema, fragment, runtimeType)) { continue; } const newDeferUsage = getDeferUsage(variableValues, selection, deferUsage); const visitedAsDeferred = visitedFragmentNames.get(fragName); let maybeNewDeferUsage; if (!newDeferUsage) { if (visitedAsDeferred === false) { continue; } visitedFragmentNames.set(fragName, false); maybeNewDeferUsage = deferUsage; } else { if (visitedAsDeferred !== undefined) { continue; } visitedFragmentNames.set(fragName, true); newDeferUsages.push(newDeferUsage); maybeNewDeferUsage = newDeferUsage; } collectFieldsImpl(context, fragment.selectionSet, groupedFieldSet, newDeferUsages, maybeNewDeferUsage); break; } } } } function getDeferUsage(variableValues, node, parentDeferUsage) { const defer = (0, utils_1.getDirectiveValues)(utils_1.GraphQLDeferDirective, node, variableValues); if (!defer || defer['if'] === false) { return; } return { label: typeof defer['label'] === 'string' ? defer['label'] : undefined, parentDeferUsage, }; } function buildExecutionPlan(originalGroupedFieldSet, parentDeferUsages = new Set()) { const groupedFieldSet = new Map(); const newGroupedFieldSets = new Map(); for (const [responseKey, fieldDetailsList] of originalGroupedFieldSet) { const filteredDeferUsageSet = getFilteredDeferUsageSet(fieldDetailsList); if ((0, isSameSet_js_1.isSameSet)(filteredDeferUsageSet, parentDeferUsages)) { groupedFieldSet.set(responseKey, fieldDetailsList); continue; } let newGroupedFieldSet = (0, getBySet_js_1.getBySet)(newGroupedFieldSets, filteredDeferUsageSet); if (newGroupedFieldSet === undefined) { newGroupedFieldSet = new Map(); newGroupedFieldSets.set(filteredDeferUsageSet, newGroupedFieldSet); } newGroupedFieldSet.set(responseKey, fieldDetailsList); } return { groupedFieldSet, newGroupedFieldSets }; } function getFilteredDeferUsageSet(fieldDetailsList) { const filteredDeferUsageSet = new Set(); for (const fieldDetails of fieldDetailsList) { const deferUsage = fieldDetails.deferUsage; if (deferUsage === undefined) { filteredDeferUsageSet.clear(); return filteredDeferUsageSet; } filteredDeferUsageSet.add(deferUsage); } for (const deferUsage of filteredDeferUsageSet) { let parentDeferUsage = deferUsage.parentDeferUsage; while (parentDeferUsage !== undefined) { if (filteredDeferUsageSet.has(parentDeferUsage)) { filteredDeferUsageSet.delete(deferUsage); break; } parentDeferUsage = parentDeferUsage.parentDeferUsage; } } return filteredDeferUsageSet; } function groupedFieldSetToFieldMap(groupedFieldSet, deferUsageByFieldNode) { const fields = new Map(); for (const [responseKey, fieldDetailsList] of groupedFieldSet) { fields.set(responseKey, fieldDetailsList.map(details => { deferUsageByFieldNode?.set(details.node, details.deferUsage); return details.node; })); } return fields; } function rootDeferUsage(deferUsageSet) { for (const usage of deferUsageSet) { if (usage.parentDeferUsage == null || !deferUsageSet.has(usage.parentDeferUsage)) { return usage; } } return undefined; } function labelForDeferUsageSet(deferUsageSet) { return rootDeferUsage(deferUsageSet)?.label; }