Skip to content

Commit d9bc448

Browse files
authored
suggestions for execution (#11)
* introduce internal getVariableSignature utility now extracted also to graphql-js PR, see graphql#4175 * execution suggestions
1 parent 47ff0fb commit d9bc448

File tree

6 files changed

+203
-210
lines changed

6 files changed

+203
-210
lines changed

src/execution/collectFields.ts

Lines changed: 58 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -22,33 +22,44 @@ import {
2222
} from '../type/directives.js';
2323
import type { GraphQLSchema } from '../type/schema.js';
2424

25+
import type { GraphQLVariableSignature } from '../utilities/getVariableSignature.js';
2526
import { typeFromAST } from '../utilities/typeFromAST.js';
2627

27-
import { getArgumentValuesFromSpread, getDirectiveValues } from './values.js';
28+
import { experimentalGetArgumentValues, getDirectiveValues } from './values.js';
2829

2930
export interface DeferUsage {
3031
label: string | undefined;
3132
parentDeferUsage: DeferUsage | undefined;
3233
}
3334

35+
export interface FragmentVariables {
36+
signatures: ObjMap<GraphQLVariableSignature>;
37+
values: ObjMap<unknown>;
38+
}
39+
3440
export interface FieldDetails {
3541
node: FieldNode;
36-
deferUsage: DeferUsage | undefined;
37-
fragmentVariableValues?: ObjMap<unknown> | undefined;
42+
deferUsage?: DeferUsage | undefined;
43+
fragmentVariables?: FragmentVariables | undefined;
3844
}
3945

4046
export type FieldGroup = ReadonlyArray<FieldDetails>;
4147

4248
export type GroupedFieldSet = ReadonlyMap<string, FieldGroup>;
4349

50+
export interface FragmentDetails {
51+
definition: FragmentDefinitionNode;
52+
variableSignatures?: ObjMap<GraphQLVariableSignature> | undefined;
53+
}
54+
4455
interface CollectFieldsContext {
4556
schema: GraphQLSchema;
46-
fragments: ObjMap<FragmentDefinitionNode>;
57+
fragments: ObjMap<FragmentDetails>;
58+
variableValues: { [variable: string]: unknown };
59+
fragmentVariableValues?: FragmentVariables;
4760
operation: OperationDefinitionNode;
4861
runtimeType: GraphQLObjectType;
4962
visitedFragmentNames: Set<string>;
50-
localVariableValues: { [variable: string]: unknown } | undefined;
51-
variableValues: { [variable: string]: unknown };
5263
}
5364

5465
/**
@@ -62,7 +73,7 @@ interface CollectFieldsContext {
6273
*/
6374
export function collectFields(
6475
schema: GraphQLSchema,
65-
fragments: ObjMap<FragmentDefinitionNode>,
76+
fragments: ObjMap<FragmentDetails>,
6677
variableValues: { [variable: string]: unknown },
6778
runtimeType: GraphQLObjectType,
6879
operation: OperationDefinitionNode,
@@ -75,10 +86,9 @@ export function collectFields(
7586
const context: CollectFieldsContext = {
7687
schema,
7788
fragments,
78-
runtimeType,
7989
variableValues,
90+
runtimeType,
8091
operation,
81-
localVariableValues: undefined,
8292
visitedFragmentNames: new Set(),
8393
};
8494

@@ -104,7 +114,7 @@ export function collectFields(
104114
// eslint-disable-next-line max-params
105115
export function collectSubfields(
106116
schema: GraphQLSchema,
107-
fragments: ObjMap<FragmentDefinitionNode>,
117+
fragments: ObjMap<FragmentDetails>,
108118
variableValues: { [variable: string]: unknown },
109119
operation: OperationDefinitionNode,
110120
returnType: GraphQLObjectType,
@@ -116,9 +126,8 @@ export function collectSubfields(
116126
const context: CollectFieldsContext = {
117127
schema,
118128
fragments,
119-
runtimeType: returnType,
120-
localVariableValues: undefined,
121129
variableValues,
130+
runtimeType: returnType,
122131
operation,
123132
visitedFragmentNames: new Set(),
124133
};
@@ -144,49 +153,40 @@ export function collectSubfields(
144153
};
145154
}
146155

156+
// eslint-disable-next-line max-params
147157
function collectFieldsImpl(
148158
context: CollectFieldsContext,
149159
selectionSet: SelectionSetNode,
150160
groupedFieldSet: AccumulatorMap<string, FieldDetails>,
151161
newDeferUsages: Array<DeferUsage>,
152162
deferUsage?: DeferUsage,
163+
fragmentVariables?: FragmentVariables,
153164
): void {
154165
const {
155166
schema,
156167
fragments,
157-
runtimeType,
158168
variableValues,
159-
localVariableValues,
169+
runtimeType,
160170
operation,
161171
visitedFragmentNames,
162172
} = context;
163173

164174
for (const selection of selectionSet.selections) {
165175
switch (selection.kind) {
166176
case Kind.FIELD: {
167-
if (
168-
!shouldIncludeNode(
169-
variableValues,
170-
selection,
171-
context.localVariableValues,
172-
)
173-
) {
177+
if (!shouldIncludeNode(selection, variableValues, fragmentVariables)) {
174178
continue;
175179
}
176180
groupedFieldSet.add(getFieldEntryKey(selection), {
177181
node: selection,
178182
deferUsage,
179-
fragmentVariableValues: localVariableValues ?? undefined,
183+
fragmentVariables,
180184
});
181185
break;
182186
}
183187
case Kind.INLINE_FRAGMENT: {
184188
if (
185-
!shouldIncludeNode(
186-
variableValues,
187-
selection,
188-
context.localVariableValues,
189-
) ||
189+
!shouldIncludeNode(selection, variableValues, fragmentVariables) ||
190190
!doesFragmentConditionMatch(schema, selection, runtimeType)
191191
) {
192192
continue;
@@ -195,9 +195,9 @@ function collectFieldsImpl(
195195
const newDeferUsage = getDeferUsage(
196196
operation,
197197
variableValues,
198+
fragmentVariables,
198199
selection,
199200
deferUsage,
200-
context.localVariableValues,
201201
);
202202

203203
if (!newDeferUsage) {
@@ -207,6 +207,7 @@ function collectFieldsImpl(
207207
groupedFieldSet,
208208
newDeferUsages,
209209
deferUsage,
210+
fragmentVariables,
210211
);
211212
} else {
212213
newDeferUsages.push(newDeferUsage);
@@ -216,78 +217,72 @@ function collectFieldsImpl(
216217
groupedFieldSet,
217218
newDeferUsages,
218219
newDeferUsage,
220+
fragmentVariables,
219221
);
220222
}
221223

222224
break;
223225
}
224226
case Kind.FRAGMENT_SPREAD: {
225-
const fragmentName = selection.name.value;
227+
const fragName = selection.name.value;
226228

227229
const newDeferUsage = getDeferUsage(
228230
operation,
229231
variableValues,
232+
fragmentVariables,
230233
selection,
231234
deferUsage,
232-
context.localVariableValues,
233235
);
234236

235237
if (
236238
!newDeferUsage &&
237-
(visitedFragmentNames.has(fragmentName) ||
238-
!shouldIncludeNode(
239-
variableValues,
240-
selection,
241-
context.localVariableValues,
242-
))
239+
(visitedFragmentNames.has(fragName) ||
240+
!shouldIncludeNode(selection, variableValues, fragmentVariables))
243241
) {
244242
continue;
245243
}
246244

247-
const fragment = fragments[fragmentName];
245+
const fragment = fragments[fragName];
248246
if (
249247
fragment == null ||
250-
!doesFragmentConditionMatch(schema, fragment, runtimeType)
248+
!doesFragmentConditionMatch(schema, fragment.definition, runtimeType)
251249
) {
252250
continue;
253251
}
254252

255-
// We need to introduce a concept of shadowing:
256-
//
257-
// - when a fragment defines a variable that is in the parent scope but not given
258-
// in the fragment-spread we need to look at this variable as undefined and check
259-
// whether the definition has a defaultValue, if not remove it from the variableValues.
260-
// - when a fragment does not define a variable we need to copy it over from the parent
261-
// scope as that variable can still get used in spreads later on in the selectionSet.
262-
// - when a value is passed in through the fragment-spread we need to copy over the key-value
263-
// into our variable-values.
264-
context.localVariableValues = fragment.variableDefinitions
265-
? getArgumentValuesFromSpread(
253+
const fragmentVariableSignatures = fragment.variableSignatures;
254+
let newFragmentVariables: FragmentVariables | undefined;
255+
if (fragmentVariableSignatures) {
256+
newFragmentVariables = {
257+
signatures: fragmentVariableSignatures,
258+
values: experimentalGetArgumentValues(
266259
selection,
267-
schema,
268-
fragment.variableDefinitions,
260+
Object.values(fragmentVariableSignatures),
269261
variableValues,
270-
context.localVariableValues,
271-
)
272-
: undefined;
262+
fragmentVariables,
263+
),
264+
};
265+
}
273266

274267
if (!newDeferUsage) {
275-
visitedFragmentNames.add(fragmentName);
268+
visitedFragmentNames.add(fragName);
276269
collectFieldsImpl(
277270
context,
278-
fragment.selectionSet,
271+
fragment.definition.selectionSet,
279272
groupedFieldSet,
280273
newDeferUsages,
281274
deferUsage,
275+
newFragmentVariables,
282276
);
283277
} else {
284278
newDeferUsages.push(newDeferUsage);
285279
collectFieldsImpl(
286280
context,
287-
fragment.selectionSet,
281+
fragment.definition.selectionSet,
288282
groupedFieldSet,
289283
newDeferUsages,
290284
newDeferUsage,
285+
newFragmentVariables,
291286
);
292287
}
293288
break;
@@ -304,15 +299,15 @@ function collectFieldsImpl(
304299
function getDeferUsage(
305300
operation: OperationDefinitionNode,
306301
variableValues: { [variable: string]: unknown },
302+
fragmentVariables: FragmentVariables | undefined,
307303
node: FragmentSpreadNode | InlineFragmentNode,
308304
parentDeferUsage: DeferUsage | undefined,
309-
fragmentVariableValues: { [variable: string]: unknown } | undefined,
310305
): DeferUsage | undefined {
311306
const defer = getDirectiveValues(
312307
GraphQLDeferDirective,
313308
node,
314309
variableValues,
315-
fragmentVariableValues,
310+
fragmentVariables,
316311
);
317312

318313
if (!defer) {
@@ -339,15 +334,15 @@ function getDeferUsage(
339334
* directives, where `@skip` has higher precedence than `@include`.
340335
*/
341336
function shouldIncludeNode(
342-
variableValues: { [variable: string]: unknown },
343337
node: FragmentSpreadNode | FieldNode | InlineFragmentNode,
344-
fragmentVariableValues: { [variable: string]: unknown } | undefined,
338+
variableValues: { [variable: string]: unknown },
339+
fragmentVariables: FragmentVariables | undefined,
345340
): boolean {
346341
const skip = getDirectiveValues(
347342
GraphQLSkipDirective,
348343
node,
349344
variableValues,
350-
fragmentVariableValues,
345+
fragmentVariables,
351346
);
352347
if (skip?.if === true) {
353348
return false;
@@ -357,7 +352,7 @@ function shouldIncludeNode(
357352
GraphQLIncludeDirective,
358353
node,
359354
variableValues,
360-
fragmentVariableValues,
355+
fragmentVariables,
361356
);
362357
if (include?.if === false) {
363358
return false;

0 commit comments

Comments
 (0)