Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

collectFields/collectSubfields cleanup prototype #3272

Merged
merged 1 commit into from Sep 24, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
65 changes: 59 additions & 6 deletions src/execution/collectFields.ts
Expand Up @@ -22,8 +22,7 @@ import { typeFromAST } from '../utilities/typeFromAST';
import { getDirectiveValues } from './values';

/**
* Given a selectionSet, adds all of the fields in that selection to
* the passed in map of fields, and returns it at the end.
* Given a selectionSet, collect all of the fields and returns it at the end.
*
* CollectFields requires the "runtime type" of an object. For a field which
* returns an Interface or Union type, the "runtime type" will be the actual
Expand All @@ -37,9 +36,64 @@ export function collectFields(
variableValues: { [variable: string]: unknown },
runtimeType: GraphQLObjectType,
selectionSet: SelectionSetNode,
): Map<string, ReadonlyArray<FieldNode>> {
const fields = new Map();
collectFieldsImpl(
schema,
fragments,
variableValues,
runtimeType,
selectionSet,
fields,
new Set(),
);
return fields;
}

/**
* Given an array of field nodes, collects all of the subfields of the passed
* in fields, and returns it at the end.
*
* CollectFields requires the "return type" of an object. For a field which
* returns an Interface or Union type, the "return type" will be the actual
* Object type returned by that field.
*
* @internal
*/
export function collectSubfields(
schema: GraphQLSchema,
fragments: ObjMap<FragmentDefinitionNode>,
variableValues: { [variable: string]: unknown },
returnType: GraphQLObjectType,
fieldNodes: ReadonlyArray<FieldNode>,
): Map<string, ReadonlyArray<FieldNode>> {
const subFieldNodes = new Map();
const visitedFragmentNames = new Set<string>();
for (const node of fieldNodes) {
if (node.selectionSet) {
collectFieldsImpl(
schema,
fragments,
variableValues,
returnType,
node.selectionSet,
subFieldNodes,
visitedFragmentNames,
);
}
}
return subFieldNodes;
}

function collectFieldsImpl(
schema: GraphQLSchema,
fragments: ObjMap<FragmentDefinitionNode>,
variableValues: { [variable: string]: unknown },
runtimeType: GraphQLObjectType,
selectionSet: SelectionSetNode,
fields: Map<string, Array<FieldNode>>,
visitedFragmentNames: Set<string>,
): Map<string, ReadonlyArray<FieldNode>> {
): void {
for (const selection of selectionSet.selections) {
switch (selection.kind) {
case Kind.FIELD: {
Expand All @@ -62,7 +116,7 @@ export function collectFields(
) {
continue;
}
collectFields(
collectFieldsImpl(
schema,
fragments,
variableValues,
Expand All @@ -89,7 +143,7 @@ export function collectFields(
) {
continue;
}
collectFields(
collectFieldsImpl(
schema,
fragments,
variableValues,
Expand All @@ -102,7 +156,6 @@ export function collectFields(
}
}
}
return fields;
}

/**
Expand Down
56 changes: 24 additions & 32 deletions src/execution/execute.ts
Expand Up @@ -54,7 +54,30 @@ import {
import { getOperationRootType } from '../utilities/getOperationRootType';

import { getVariableValues, getArgumentValues } from './values';
import { collectFields } from './collectFields';
import {
collectFields,
collectSubfields as _collectSubfields,
} from './collectFields';

/**
* A memoized collection of relevant subfields with regard to the return
* type. Memoizing ensures the subfields are not repeatedly calculated, which
* saves overhead when resolving lists of values.
*/
const collectSubfields = memoize3(
(
exeContext: ExecutionContext,
returnType: GraphQLObjectType,
fieldNodes: ReadonlyArray<FieldNode>,
) =>
_collectSubfields(
exeContext.schema,
exeContext.fragments,
exeContext.variableValues,
returnType,
fieldNodes,
),
);

/**
* Terminology
Expand Down Expand Up @@ -330,8 +353,6 @@ function executeOperation(
exeContext.variableValues,
type,
operation.selectionSet,
new Map(),
new Set(),
);

const path = undefined;
Expand Down Expand Up @@ -921,35 +942,6 @@ function invalidReturnTypeError(
);
}

/**
* A memoized collection of relevant subfields with regard to the return
* type. Memoizing ensures the subfields are not repeatedly calculated, which
* saves overhead when resolving lists of values.
*/
const collectSubfields = memoize3(_collectSubfields);
function _collectSubfields(
exeContext: ExecutionContext,
returnType: GraphQLObjectType,
fieldNodes: ReadonlyArray<FieldNode>,
): Map<string, ReadonlyArray<FieldNode>> {
let subFieldNodes = new Map();
const visitedFragmentNames = new Set<string>();
for (const node of fieldNodes) {
if (node.selectionSet) {
subFieldNodes = collectFields(
exeContext.schema,
exeContext.fragments,
exeContext.variableValues,
returnType,
node.selectionSet,
subFieldNodes,
visitedFragmentNames,
);
}
}
return subFieldNodes;
}

/**
* If a resolveType function is not given, then a default resolve behavior is
* used which attempts two strategies:
Expand Down
2 changes: 0 additions & 2 deletions src/subscription/subscribe.ts
Expand Up @@ -198,8 +198,6 @@ async function executeSubscription(
variableValues,
type,
operation.selectionSet,
new Map(),
new Set(),
);
const [responseName, fieldNodes] = [...fields.entries()][0];
const fieldDef = getFieldDef(schema, type, fieldNodes[0]);
Expand Down
2 changes: 0 additions & 2 deletions src/validation/rules/SingleFieldSubscriptionsRule.ts
Expand Up @@ -44,8 +44,6 @@ export function SingleFieldSubscriptionsRule(
variableValues,
subscriptionType,
node.selectionSet,
new Map(),
new Set(),
);
if (fields.size > 1) {
const fieldSelectionLists = [...fields.values()];
Expand Down