Skip to content

Commit 3450cb8

Browse files
committed
collectFields/collectSubfields refactor and export as part of public API
Motivated by graphql#3246
1 parent a05dcfc commit 3450cb8

File tree

4 files changed

+83
-42
lines changed

4 files changed

+83
-42
lines changed

src/execution/collectFields.ts

Lines changed: 59 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,7 @@ import { typeFromAST } from '../utilities/typeFromAST';
2222
import { getDirectiveValues } from './values';
2323

2424
/**
25-
* Given a selectionSet, adds all of the fields in that selection to
26-
* the passed in map of fields, and returns it at the end.
25+
* Given a selectionSet, collect all of the fields and returns it at the end.
2726
*
2827
* CollectFields requires the "runtime type" of an object. For a field which
2928
* returns an Interface or Union type, the "runtime type" will be the actual
@@ -37,9 +36,64 @@ export function collectFields(
3736
variableValues: { [variable: string]: unknown },
3837
runtimeType: GraphQLObjectType,
3938
selectionSet: SelectionSetNode,
39+
): Map<string, ReadonlyArray<FieldNode>> {
40+
const fields = new Map();
41+
collectFieldsImpl(
42+
schema,
43+
fragments,
44+
variableValues,
45+
runtimeType,
46+
selectionSet,
47+
fields,
48+
new Set(),
49+
);
50+
return fields;
51+
}
52+
53+
/**
54+
* Given an array of field nodes, collects all of the subfields of the passed
55+
* in fields, and returns it at the end.
56+
*
57+
* CollectFields requires the "return type" of an object. For a field which
58+
* returns an Interface or Union type, the "return type" will be the actual
59+
* Object type returned by that field.
60+
*
61+
* @internal
62+
*/
63+
export function collectSubfields(
64+
schema: GraphQLSchema,
65+
fragments: ObjMap<FragmentDefinitionNode>,
66+
variableValues: { [variable: string]: unknown },
67+
returnType: GraphQLObjectType,
68+
fieldNodes: ReadonlyArray<FieldNode>,
69+
): Map<string, ReadonlyArray<FieldNode>> {
70+
const subFieldNodes = new Map();
71+
const visitedFragmentNames = new Set<string>();
72+
for (const node of fieldNodes) {
73+
if (node.selectionSet) {
74+
collectFieldsImpl(
75+
schema,
76+
fragments,
77+
variableValues,
78+
returnType,
79+
node.selectionSet,
80+
subFieldNodes,
81+
visitedFragmentNames,
82+
);
83+
}
84+
}
85+
return subFieldNodes;
86+
}
87+
88+
function collectFieldsImpl(
89+
schema: GraphQLSchema,
90+
fragments: ObjMap<FragmentDefinitionNode>,
91+
variableValues: { [variable: string]: unknown },
92+
runtimeType: GraphQLObjectType,
93+
selectionSet: SelectionSetNode,
4094
fields: Map<string, Array<FieldNode>>,
4195
visitedFragmentNames: Set<string>,
42-
): Map<string, ReadonlyArray<FieldNode>> {
96+
): void {
4397
for (const selection of selectionSet.selections) {
4498
switch (selection.kind) {
4599
case Kind.FIELD: {
@@ -62,7 +116,7 @@ export function collectFields(
62116
) {
63117
continue;
64118
}
65-
collectFields(
119+
collectFieldsImpl(
66120
schema,
67121
fragments,
68122
variableValues,
@@ -89,7 +143,7 @@ export function collectFields(
89143
) {
90144
continue;
91145
}
92-
collectFields(
146+
collectFieldsImpl(
93147
schema,
94148
fragments,
95149
variableValues,
@@ -102,7 +156,6 @@ export function collectFields(
102156
}
103157
}
104158
}
105-
return fields;
106159
}
107160

108161
/**

src/execution/execute.ts

Lines changed: 24 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,30 @@ import {
5454
import { getOperationRootType } from '../utilities/getOperationRootType';
5555

5656
import { getVariableValues, getArgumentValues } from './values';
57-
import { collectFields } from './collectFields';
57+
import {
58+
collectFields,
59+
collectSubfields as _collectSubfields,
60+
} from './collectFields';
61+
62+
/**
63+
* A memoized collection of relevant subfields with regard to the return
64+
* type. Memoizing ensures the subfields are not repeatedly calculated, which
65+
* saves overhead when resolving lists of values.
66+
*/
67+
const collectSubfields = memoize3(
68+
(
69+
exeContext: ExecutionContext,
70+
returnType: GraphQLObjectType,
71+
fieldNodes: ReadonlyArray<FieldNode>,
72+
) =>
73+
_collectSubfields(
74+
exeContext.schema,
75+
exeContext.fragments,
76+
exeContext.variableValues,
77+
returnType,
78+
fieldNodes,
79+
),
80+
);
5881

5982
/**
6083
* Terminology
@@ -330,8 +353,6 @@ function executeOperation(
330353
exeContext.variableValues,
331354
type,
332355
operation.selectionSet,
333-
new Map(),
334-
new Set(),
335356
);
336357

337358
const path = undefined;
@@ -921,35 +942,6 @@ function invalidReturnTypeError(
921942
);
922943
}
923944

924-
/**
925-
* A memoized collection of relevant subfields with regard to the return
926-
* type. Memoizing ensures the subfields are not repeatedly calculated, which
927-
* saves overhead when resolving lists of values.
928-
*/
929-
const collectSubfields = memoize3(_collectSubfields);
930-
function _collectSubfields(
931-
exeContext: ExecutionContext,
932-
returnType: GraphQLObjectType,
933-
fieldNodes: ReadonlyArray<FieldNode>,
934-
): Map<string, ReadonlyArray<FieldNode>> {
935-
let subFieldNodes = new Map();
936-
const visitedFragmentNames = new Set<string>();
937-
for (const node of fieldNodes) {
938-
if (node.selectionSet) {
939-
subFieldNodes = collectFields(
940-
exeContext.schema,
941-
exeContext.fragments,
942-
exeContext.variableValues,
943-
returnType,
944-
node.selectionSet,
945-
subFieldNodes,
946-
visitedFragmentNames,
947-
);
948-
}
949-
}
950-
return subFieldNodes;
951-
}
952-
953945
/**
954946
* If a resolveType function is not given, then a default resolve behavior is
955947
* used which attempts two strategies:

src/subscription/subscribe.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -198,8 +198,6 @@ async function executeSubscription(
198198
variableValues,
199199
type,
200200
operation.selectionSet,
201-
new Map(),
202-
new Set(),
203201
);
204202
const [responseName, fieldNodes] = [...fields.entries()][0];
205203
const fieldDef = getFieldDef(schema, type, fieldNodes[0]);

src/validation/rules/SingleFieldSubscriptionsRule.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,6 @@ export function SingleFieldSubscriptionsRule(
4444
variableValues,
4545
subscriptionType,
4646
node.selectionSet,
47-
new Map(),
48-
new Set(),
4947
);
5048
if (fields.size > 1) {
5149
const fieldSelectionLists = [...fields.values()];

0 commit comments

Comments
 (0)