diff --git a/src/execution/execute.js b/src/execution/execute.js index 5137a5b1f7..f87cb76df0 100644 --- a/src/execution/execute.js +++ b/src/execution/execute.js @@ -430,7 +430,11 @@ function doesFragmentConditionMatch( fragment: FragmentDefinition | InlineFragment, type: GraphQLObjectType ): boolean { - var conditionalType = typeFromAST(exeContext.schema, fragment.typeCondition); + var typeConditionAST = fragment.typeCondition; + if (!typeConditionAST) { + return true; + } + var conditionalType = typeFromAST(exeContext.schema, typeConditionAST); if (conditionalType === type) { return true; } diff --git a/src/language/__tests__/kitchen-sink.graphql b/src/language/__tests__/kitchen-sink.graphql index 0b05f1db3e..0d2ceff043 100644 --- a/src/language/__tests__/kitchen-sink.graphql +++ b/src/language/__tests__/kitchen-sink.graphql @@ -17,6 +17,12 @@ query queryName($foo: ComplexType, $site: Site = MOBILE) { } } } + ... @skip(unless: $foo) { + id + } + ... { + id + } } } diff --git a/src/language/__tests__/printer.js b/src/language/__tests__/printer.js index a522de014c..1d25bd3791 100644 --- a/src/language/__tests__/printer.js +++ b/src/language/__tests__/printer.js @@ -59,6 +59,12 @@ describe('Printer', () => { } } } + ... @skip(unless: $foo) { + id + } + ... { + id + } } } diff --git a/src/language/__tests__/visitor.js b/src/language/__tests__/visitor.js index 0b4604c7de..b191384bad 100644 --- a/src/language/__tests__/visitor.js +++ b/src/language/__tests__/visitor.js @@ -321,6 +321,34 @@ describe('Visitor', () => { [ 'leave', 'Field', 0, undefined ], [ 'leave', 'SelectionSet', 'selectionSet', 'InlineFragment' ], [ 'leave', 'InlineFragment', 1, undefined ], + [ 'enter', 'InlineFragment', 2, undefined ], + [ 'enter', 'Directive', 0, undefined ], + [ 'enter', 'Name', 'name', 'Directive' ], + [ 'leave', 'Name', 'name', 'Directive' ], + [ 'enter', 'Argument', 0, undefined ], + [ 'enter', 'Name', 'name', 'Argument' ], + [ 'leave', 'Name', 'name', 'Argument' ], + [ 'enter', 'Variable', 'value', 'Argument' ], + [ 'enter', 'Name', 'name', 'Variable' ], + [ 'leave', 'Name', 'name', 'Variable' ], + [ 'leave', 'Variable', 'value', 'Argument' ], + [ 'leave', 'Argument', 0, undefined ], + [ 'leave', 'Directive', 0, undefined ], + [ 'enter', 'SelectionSet', 'selectionSet', 'InlineFragment' ], + [ 'enter', 'Field', 0, undefined ], + [ 'enter', 'Name', 'name', 'Field' ], + [ 'leave', 'Name', 'name', 'Field' ], + [ 'leave', 'Field', 0, undefined ], + [ 'leave', 'SelectionSet', 'selectionSet', 'InlineFragment' ], + [ 'leave', 'InlineFragment', 2, undefined ], + [ 'enter', 'InlineFragment', 3, undefined ], + [ 'enter', 'SelectionSet', 'selectionSet', 'InlineFragment' ], + [ 'enter', 'Field', 0, undefined ], + [ 'enter', 'Name', 'name', 'Field' ], + [ 'leave', 'Name', 'name', 'Field' ], + [ 'leave', 'Field', 0, undefined ], + [ 'leave', 'SelectionSet', 'selectionSet', 'InlineFragment' ], + [ 'leave', 'InlineFragment', 3, undefined ], [ 'leave', 'SelectionSet', 'selectionSet', 'Field' ], [ 'leave', 'Field', 0, undefined ], [ 'leave', 'SelectionSet', 'selectionSet', 'OperationDefinition' ], diff --git a/src/language/ast.js b/src/language/ast.js index 8cc5d83206..28c42a864c 100644 --- a/src/language/ast.js +++ b/src/language/ast.js @@ -142,7 +142,7 @@ export type FragmentSpread = { export type InlineFragment = { kind: 'InlineFragment'; loc?: ?Location; - typeCondition: NamedType; + typeCondition?: ?NamedType; directives?: ?Array; selectionSet: SelectionSet; } diff --git a/src/language/parser.js b/src/language/parser.js index 68fe3ea462..f2322691f2 100644 --- a/src/language/parser.js +++ b/src/language/parser.js @@ -377,25 +377,29 @@ function parseArgument(parser): Argument { * * FragmentSpread : ... FragmentName Directives? * - * InlineFragment : ... on TypeCondition Directives? SelectionSet + * InlineFragment : ... TypeCondition? Directives? SelectionSet */ function parseFragment(parser): FragmentSpread | InlineFragment { var start = parser.token.start; expect(parser, TokenKind.SPREAD); - if (parser.token.value === 'on') { - advance(parser); + if (peek(parser, TokenKind.NAME) && parser.token.value !== 'on') { return { - kind: INLINE_FRAGMENT, - typeCondition: parseNamedType(parser), + kind: FRAGMENT_SPREAD, + name: parseFragmentName(parser), directives: parseDirectives(parser), - selectionSet: parseSelectionSet(parser), loc: loc(parser, start) }; } + var typeCondition = null; + if (parser.token.value === 'on') { + advance(parser); + typeCondition = parseNamedType(parser); + } return { - kind: FRAGMENT_SPREAD, - name: parseFragmentName(parser), + kind: INLINE_FRAGMENT, + typeCondition, directives: parseDirectives(parser), + selectionSet: parseSelectionSet(parser), loc: loc(parser, start) }; } diff --git a/src/language/printer.js b/src/language/printer.js index c4ffec5271..4d1f083d06 100644 --- a/src/language/printer.js +++ b/src/language/printer.js @@ -55,9 +55,12 @@ var printDocASTReducer = { '...' + name + wrap(' ', join(directives, ' ')), InlineFragment: ({ typeCondition, directives, selectionSet }) => - `... on ${typeCondition} ` + - wrap('', join(directives, ' '), ' ') + - selectionSet, + join([ + '...', + wrap('on ', typeCondition), + join(directives, ' '), + selectionSet + ], ' '), FragmentDefinition: ({ name, typeCondition, directives, selectionSet }) => `fragment ${name} on ${typeCondition} ` + diff --git a/src/utilities/TypeInfo.js b/src/utilities/TypeInfo.js index 694615cae7..d7b0e5b34b 100644 --- a/src/utilities/TypeInfo.js +++ b/src/utilities/TypeInfo.js @@ -132,7 +132,10 @@ export class TypeInfo { break; case Kind.INLINE_FRAGMENT: case Kind.FRAGMENT_DEFINITION: - type = typeFromAST(schema, node.typeCondition); + var typeConditionAST = node.typeCondition; + type = typeConditionAST ? + typeFromAST(schema, typeConditionAST) : + this.getType(); this._typeStack.push(type); break; case Kind.VARIABLE_DEFINITION: diff --git a/src/validation/__tests__/FieldsOnCorrectType.js b/src/validation/__tests__/FieldsOnCorrectType.js index 943276aede..e2ac55e459 100644 --- a/src/validation/__tests__/FieldsOnCorrectType.js +++ b/src/validation/__tests__/FieldsOnCorrectType.js @@ -185,6 +185,9 @@ describe('Validate: Fields on correct type', () => { ... on Dog { name } + ... { + name + } } `); }); diff --git a/src/validation/__tests__/KnownFragmentNames.js b/src/validation/__tests__/KnownFragmentNames.js index 8105cbde5a..a05016f64f 100644 --- a/src/validation/__tests__/KnownFragmentNames.js +++ b/src/validation/__tests__/KnownFragmentNames.js @@ -32,6 +32,9 @@ describe('Validate: Known fragment names', () => { ... on Human { ...HumanFields2 } + ... { + name + } } } fragment HumanFields1 on Human { diff --git a/src/validation/__tests__/KnownTypeNames.js b/src/validation/__tests__/KnownTypeNames.js index 7ba306b62f..e5cfee09c0 100644 --- a/src/validation/__tests__/KnownTypeNames.js +++ b/src/validation/__tests__/KnownTypeNames.js @@ -28,7 +28,7 @@ describe('Validate: Known type names', () => { expectPassesRule(KnownTypeNames, ` query Foo($var: String, $required: [String!]!) { user(id: 4) { - pets { ... on Pet { name }, ...PetFields } + pets { ... on Pet { name }, ...PetFields, ... { name } } } } fragment PetFields on Pet {