Skip to content

Commit 1105999

Browse files
committed
polish(fragmentArgs): simplify fragment args execution
by creating a map for each fragment with fragment variables with the properly combined local and global variables background: this was in the original PR graphql#4015, was taken out by me when i misunderstood and thought that when a fragment variable shadows an operation variable, the operation variable could still leak through if the fragment variable had no default, and I thought it was necessary within getArgumentValues to still have the signatures advantages to the original approach, which this PR restores: does not change the signature of getVariableValues, getDirectiveValues, valueFromAST disadvantages: creating a combined variable map of local and global variables may be a waste if the global variables are not actually used in that fragment
1 parent 1dbdadc commit 1105999

File tree

4 files changed

+46
-98
lines changed

4 files changed

+46
-98
lines changed

src/execution/collectFields.ts

Lines changed: 32 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,10 @@ export interface DeferUsage {
3232
parentDeferUsage: DeferUsage | undefined;
3333
}
3434

35-
export interface FragmentVariables {
36-
signatures: ObjMap<GraphQLVariableSignature>;
37-
values: ObjMap<unknown>;
38-
}
39-
4035
export interface FieldDetails {
4136
node: FieldNode;
4237
deferUsage?: DeferUsage | undefined;
43-
fragmentVariables?: FragmentVariables | undefined;
38+
fragmentVariableValues?: { [variable: string]: unknown } | undefined;
4439
}
4540

4641
export type FieldGroup = ReadonlyArray<FieldDetails>;
@@ -136,14 +131,14 @@ export function collectSubfields(
136131
for (const fieldDetail of fieldGroup) {
137132
const selectionSet = fieldDetail.node.selectionSet;
138133
if (selectionSet) {
139-
const { deferUsage, fragmentVariables } = fieldDetail;
134+
const { deferUsage, fragmentVariableValues } = fieldDetail;
140135
collectFieldsImpl(
141136
context,
142137
selectionSet,
143138
subGroupedFieldSet,
144139
newDeferUsages,
145140
deferUsage,
146-
fragmentVariables,
141+
fragmentVariableValues,
147142
);
148143
}
149144
}
@@ -161,7 +156,7 @@ function collectFieldsImpl(
161156
groupedFieldSet: AccumulatorMap<string, FieldDetails>,
162157
newDeferUsages: Array<DeferUsage>,
163158
deferUsage?: DeferUsage,
164-
fragmentVariables?: FragmentVariables,
159+
fragmentVariableValues?: { [variable: string]: unknown },
165160
): void {
166161
const {
167162
schema,
@@ -172,31 +167,32 @@ function collectFieldsImpl(
172167
visitedFragmentNames,
173168
} = context;
174169

170+
const scopedVariableValues = fragmentVariableValues ?? variableValues;
171+
175172
for (const selection of selectionSet.selections) {
176173
switch (selection.kind) {
177174
case Kind.FIELD: {
178-
if (!shouldIncludeNode(selection, variableValues, fragmentVariables)) {
175+
if (!shouldIncludeNode(scopedVariableValues, selection)) {
179176
continue;
180177
}
181178
groupedFieldSet.add(getFieldEntryKey(selection), {
182179
node: selection,
183180
deferUsage,
184-
fragmentVariables,
181+
fragmentVariableValues,
185182
});
186183
break;
187184
}
188185
case Kind.INLINE_FRAGMENT: {
189186
if (
190-
!shouldIncludeNode(selection, variableValues, fragmentVariables) ||
187+
!shouldIncludeNode(scopedVariableValues, selection) ||
191188
!doesFragmentConditionMatch(schema, selection, runtimeType)
192189
) {
193190
continue;
194191
}
195192

196193
const newDeferUsage = getDeferUsage(
197194
operation,
198-
variableValues,
199-
fragmentVariables,
195+
scopedVariableValues,
200196
selection,
201197
deferUsage,
202198
);
@@ -208,7 +204,7 @@ function collectFieldsImpl(
208204
groupedFieldSet,
209205
newDeferUsages,
210206
deferUsage,
211-
fragmentVariables,
207+
fragmentVariableValues,
212208
);
213209
} else {
214210
newDeferUsages.push(newDeferUsage);
@@ -218,7 +214,7 @@ function collectFieldsImpl(
218214
groupedFieldSet,
219215
newDeferUsages,
220216
newDeferUsage,
221-
fragmentVariables,
217+
fragmentVariableValues,
222218
);
223219
}
224220

@@ -229,16 +225,15 @@ function collectFieldsImpl(
229225

230226
const newDeferUsage = getDeferUsage(
231227
operation,
232-
variableValues,
233-
fragmentVariables,
228+
scopedVariableValues,
234229
selection,
235230
deferUsage,
236231
);
237232

238233
if (
239234
!newDeferUsage &&
240235
(visitedFragmentNames.has(fragName) ||
241-
!shouldIncludeNode(selection, variableValues, fragmentVariables))
236+
!shouldIncludeNode(scopedVariableValues, selection))
242237
) {
243238
continue;
244239
}
@@ -252,17 +247,20 @@ function collectFieldsImpl(
252247
}
253248

254249
const fragmentVariableSignatures = fragment.variableSignatures;
255-
let newFragmentVariables: FragmentVariables | undefined;
250+
let newFragmentVariableValues:
251+
| { [variable: string]: unknown }
252+
| undefined;
256253
if (fragmentVariableSignatures) {
257-
newFragmentVariables = {
258-
signatures: fragmentVariableSignatures,
259-
values: experimentalGetArgumentValues(
260-
selection,
261-
Object.values(fragmentVariableSignatures),
262-
variableValues,
263-
fragmentVariables,
264-
),
265-
};
254+
newFragmentVariableValues = experimentalGetArgumentValues(
255+
selection,
256+
Object.values(fragmentVariableSignatures),
257+
scopedVariableValues,
258+
);
259+
for (const [variableName, value] of Object.entries(variableValues)) {
260+
if (!fragment.variableSignatures?.[variableName]) {
261+
newFragmentVariableValues[variableName] = value;
262+
}
263+
}
266264
}
267265

268266
if (!newDeferUsage) {
@@ -273,7 +271,7 @@ function collectFieldsImpl(
273271
groupedFieldSet,
274272
newDeferUsages,
275273
deferUsage,
276-
newFragmentVariables,
274+
newFragmentVariableValues,
277275
);
278276
} else {
279277
newDeferUsages.push(newDeferUsage);
@@ -283,7 +281,7 @@ function collectFieldsImpl(
283281
groupedFieldSet,
284282
newDeferUsages,
285283
newDeferUsage,
286-
newFragmentVariables,
284+
newFragmentVariableValues,
287285
);
288286
}
289287
break;
@@ -300,16 +298,10 @@ function collectFieldsImpl(
300298
function getDeferUsage(
301299
operation: OperationDefinitionNode,
302300
variableValues: { [variable: string]: unknown },
303-
fragmentVariables: FragmentVariables | undefined,
304301
node: FragmentSpreadNode | InlineFragmentNode,
305302
parentDeferUsage: DeferUsage | undefined,
306303
): DeferUsage | undefined {
307-
const defer = getDirectiveValues(
308-
GraphQLDeferDirective,
309-
node,
310-
variableValues,
311-
fragmentVariables,
312-
);
304+
const defer = getDirectiveValues(GraphQLDeferDirective, node, variableValues);
313305

314306
if (!defer) {
315307
return;
@@ -335,16 +327,10 @@ function getDeferUsage(
335327
* directives, where `@skip` has higher precedence than `@include`.
336328
*/
337329
function shouldIncludeNode(
338-
node: FragmentSpreadNode | FieldNode | InlineFragmentNode,
339330
variableValues: { [variable: string]: unknown },
340-
fragmentVariables: FragmentVariables | undefined,
331+
node: FragmentSpreadNode | FieldNode | InlineFragmentNode,
341332
): boolean {
342-
const skip = getDirectiveValues(
343-
GraphQLSkipDirective,
344-
node,
345-
variableValues,
346-
fragmentVariables,
347-
);
333+
const skip = getDirectiveValues(GraphQLSkipDirective, node, variableValues);
348334
if (skip?.if === true) {
349335
return false;
350336
}
@@ -353,7 +339,6 @@ function shouldIncludeNode(
353339
GraphQLIncludeDirective,
354340
node,
355341
variableValues,
356-
fragmentVariables,
357342
);
358343
if (include?.if === false) {
359344
return false;

src/execution/execute.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -752,8 +752,7 @@ function executeField(
752752
const args = experimentalGetArgumentValues(
753753
fieldGroup[0].node,
754754
fieldDef.args,
755-
exeContext.variableValues,
756-
fieldGroup[0].fragmentVariables,
755+
fieldGroup[0].fragmentVariableValues ?? exeContext.variableValues,
757756
);
758757

759758
// The resolve function's optional third argument is a context value that
@@ -1061,8 +1060,7 @@ function getStreamUsage(
10611060
const stream = getDirectiveValues(
10621061
GraphQLStreamDirective,
10631062
fieldGroup[0].node,
1064-
exeContext.variableValues,
1065-
fieldGroup[0].fragmentVariables,
1063+
fieldGroup[0].fragmentVariableValues ?? exeContext.variableValues,
10661064
);
10671065

10681066
if (!stream) {
@@ -1091,7 +1089,7 @@ function getStreamUsage(
10911089
const streamedFieldGroup: FieldGroup = fieldGroup.map((fieldDetails) => ({
10921090
node: fieldDetails.node,
10931091
deferUsage: undefined,
1094-
fragmentVariables: fieldDetails.fragmentVariables,
1092+
fragmentVariableValues: fieldDetails.fragmentVariableValues,
10951093
}));
10961094

10971095
const streamUsage = {

src/execution/values.ts

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ import type { GraphQLSchema } from '../type/schema.js';
2222
import { coerceInputValue } from '../utilities/coerceInputValue.js';
2323
import { valueFromAST } from '../utilities/valueFromAST.js';
2424

25-
import type { FragmentVariables } from './collectFields.js';
2625
import type { GraphQLVariableSignature } from './getVariableSignature.js';
2726
import { getVariableSignature } from './getVariableSignature.js';
2827

@@ -156,7 +155,6 @@ export function experimentalGetArgumentValues(
156155
node: FieldNode | DirectiveNode | FragmentSpreadNode,
157156
argDefs: ReadonlyArray<GraphQLArgument | GraphQLVariableSignature>,
158157
variableValues: Maybe<ObjMap<unknown>>,
159-
fragmentVariables?: Maybe<FragmentVariables>,
160158
): { [argument: string]: unknown } {
161159
const coercedValues: { [argument: string]: unknown } = {};
162160

@@ -188,12 +186,9 @@ export function experimentalGetArgumentValues(
188186

189187
if (valueNode.kind === Kind.VARIABLE) {
190188
const variableName = valueNode.name.value;
191-
const scopedVariableValues = fragmentVariables?.signatures[variableName]
192-
? fragmentVariables.values
193-
: variableValues;
194189
if (
195-
scopedVariableValues == null ||
196-
!Object.hasOwn(scopedVariableValues, variableName)
190+
variableValues == null ||
191+
!Object.hasOwn(variableValues, variableName)
197192
) {
198193
if (argDef.defaultValue !== undefined) {
199194
coercedValues[name] = argDef.defaultValue;
@@ -206,7 +201,7 @@ export function experimentalGetArgumentValues(
206201
}
207202
continue;
208203
}
209-
isNull = scopedVariableValues[variableName] == null;
204+
isNull = variableValues[variableName] == null;
210205
}
211206

212207
if (isNull && isNonNullType(argType)) {
@@ -217,12 +212,7 @@ export function experimentalGetArgumentValues(
217212
);
218213
}
219214

220-
const coercedValue = valueFromAST(
221-
valueNode,
222-
argType,
223-
variableValues,
224-
fragmentVariables?.values,
225-
);
215+
const coercedValue = valueFromAST(valueNode, argType, variableValues);
226216
if (coercedValue === undefined) {
227217
// Note: ValuesOfCorrectTypeRule validation should catch this before
228218
// execution. This is a runtime check to ensure execution does not
@@ -254,7 +244,6 @@ export function getDirectiveValues(
254244
directiveDef: GraphQLDirective,
255245
node: { readonly directives?: ReadonlyArray<DirectiveNode> | undefined },
256246
variableValues?: Maybe<ObjMap<unknown>>,
257-
fragmentVariables?: Maybe<FragmentVariables>,
258247
): undefined | { [argument: string]: unknown } {
259248
const directiveNode = node.directives?.find(
260249
(directive) => directive.name.value === directiveDef.name,
@@ -265,7 +254,6 @@ export function getDirectiveValues(
265254
directiveNode,
266255
directiveDef.args,
267256
variableValues,
268-
fragmentVariables,
269257
);
270258
}
271259
}

0 commit comments

Comments
 (0)