@@ -20,10 +20,7 @@ import { locatedError } from '../error/locatedError';
2020import type {
2121 DocumentNode ,
2222 OperationDefinitionNode ,
23- SelectionSetNode ,
2423 FieldNode ,
25- FragmentSpreadNode ,
26- InlineFragmentNode ,
2724 FragmentDefinitionNode ,
2825} from '../language/ast' ;
2926import { Kind } from '../language/kinds' ;
@@ -46,10 +43,6 @@ import {
4643 TypeMetaFieldDef ,
4744 TypeNameMetaFieldDef ,
4845} from '../type/introspection' ;
49- import {
50- GraphQLIncludeDirective ,
51- GraphQLSkipDirective ,
52- } from '../type/directives' ;
5346import {
5447 isObjectType ,
5548 isAbstractType ,
@@ -58,14 +51,10 @@ import {
5851 isNonNullType ,
5952} from '../type/definition' ;
6053
61- import { typeFromAST } from '../utilities/typeFromAST' ;
6254import { getOperationRootType } from '../utilities/getOperationRootType' ;
6355
64- import {
65- getVariableValues ,
66- getArgumentValues ,
67- getDirectiveValues ,
68- } from './values' ;
56+ import { getVariableValues , getArgumentValues } from './values' ;
57+ import { collectFields } from './collectFields' ;
6958
7059/**
7160 * Terminology
@@ -336,7 +325,9 @@ function executeOperation(
336325) : PromiseOrValue < ObjMap < unknown > | null > {
337326 const type = getOperationRootType ( exeContext . schema , operation ) ;
338327 const fields = collectFields (
339- exeContext ,
328+ exeContext . schema ,
329+ exeContext . fragments ,
330+ exeContext . variableValues ,
340331 type ,
341332 operation . selectionSet ,
342333 new Map ( ) ,
@@ -447,141 +438,6 @@ function executeFields(
447438 return promiseForObject ( results ) ;
448439}
449440
450- /**
451- * Given a selectionSet, adds all of the fields in that selection to
452- * the passed in map of fields, and returns it at the end.
453- *
454- * CollectFields requires the "runtime type" of an object. For a field which
455- * returns an Interface or Union type, the "runtime type" will be the actual
456- * Object type returned by that field.
457- *
458- * @internal
459- */
460- export function collectFields (
461- exeContext : ExecutionContext ,
462- runtimeType : GraphQLObjectType ,
463- selectionSet : SelectionSetNode ,
464- fields : Map < string , Array < FieldNode > > ,
465- visitedFragmentNames : Set < string > ,
466- ) : Map < string , ReadonlyArray < FieldNode > > {
467- for ( const selection of selectionSet . selections ) {
468- switch ( selection . kind ) {
469- case Kind . FIELD : {
470- if ( ! shouldIncludeNode ( exeContext , selection ) ) {
471- continue ;
472- }
473- const name = getFieldEntryKey ( selection ) ;
474- const fieldList = fields . get ( name ) ;
475- if ( fieldList !== undefined ) {
476- fieldList . push ( selection ) ;
477- } else {
478- fields . set ( name , [ selection ] ) ;
479- }
480- break ;
481- }
482- case Kind . INLINE_FRAGMENT : {
483- if (
484- ! shouldIncludeNode ( exeContext , selection ) ||
485- ! doesFragmentConditionMatch ( exeContext , selection , runtimeType )
486- ) {
487- continue ;
488- }
489- collectFields (
490- exeContext ,
491- runtimeType ,
492- selection . selectionSet ,
493- fields ,
494- visitedFragmentNames ,
495- ) ;
496- break ;
497- }
498- case Kind . FRAGMENT_SPREAD : {
499- const fragName = selection . name . value ;
500- if (
501- visitedFragmentNames . has ( fragName ) ||
502- ! shouldIncludeNode ( exeContext , selection )
503- ) {
504- continue ;
505- }
506- visitedFragmentNames . add ( fragName ) ;
507- const fragment = exeContext . fragments [ fragName ] ;
508- if (
509- ! fragment ||
510- ! doesFragmentConditionMatch ( exeContext , fragment , runtimeType )
511- ) {
512- continue ;
513- }
514- collectFields (
515- exeContext ,
516- runtimeType ,
517- fragment . selectionSet ,
518- fields ,
519- visitedFragmentNames ,
520- ) ;
521- break ;
522- }
523- }
524- }
525- return fields ;
526- }
527-
528- /**
529- * Determines if a field should be included based on the @include and @skip
530- * directives, where @skip has higher precedence than @include.
531- */
532- function shouldIncludeNode (
533- exeContext : ExecutionContext ,
534- node : FragmentSpreadNode | FieldNode | InlineFragmentNode ,
535- ) : boolean {
536- const skip = getDirectiveValues (
537- GraphQLSkipDirective ,
538- node ,
539- exeContext . variableValues ,
540- ) ;
541- if ( skip ?. if === true ) {
542- return false ;
543- }
544-
545- const include = getDirectiveValues (
546- GraphQLIncludeDirective ,
547- node ,
548- exeContext . variableValues ,
549- ) ;
550- if ( include ?. if === false ) {
551- return false ;
552- }
553- return true ;
554- }
555-
556- /**
557- * Determines if a fragment is applicable to the given type.
558- */
559- function doesFragmentConditionMatch (
560- exeContext : ExecutionContext ,
561- fragment : FragmentDefinitionNode | InlineFragmentNode ,
562- type : GraphQLObjectType ,
563- ) : boolean {
564- const typeConditionNode = fragment . typeCondition ;
565- if ( ! typeConditionNode ) {
566- return true ;
567- }
568- const conditionalType = typeFromAST ( exeContext . schema , typeConditionNode ) ;
569- if ( conditionalType === type ) {
570- return true ;
571- }
572- if ( isAbstractType ( conditionalType ) ) {
573- return exeContext . schema . isSubType ( conditionalType , type ) ;
574- }
575- return false ;
576- }
577-
578- /**
579- * Implements the logic to compute the key of a given field's entry
580- */
581- function getFieldEntryKey ( node : FieldNode ) : string {
582- return node . alias ? node . alias . value : node . name . value ;
583- }
584-
585441/**
586442 * Implements the "Executing field" section of the spec
587443 * In particular, this function figures out the value that the field returns by
@@ -1081,7 +937,9 @@ function _collectSubfields(
1081937 for ( const node of fieldNodes ) {
1082938 if ( node . selectionSet ) {
1083939 subFieldNodes = collectFields (
1084- exeContext ,
940+ exeContext . schema ,
941+ exeContext . fragments ,
942+ exeContext . variableValues ,
1085943 returnType ,
1086944 node . selectionSet ,
1087945 subFieldNodes ,
0 commit comments