diff --git a/src/language/__tests__/visitor-test.js b/src/language/__tests__/visitor-test.js index 65fbda7460..1d58de3dd6 100644 --- a/src/language/__tests__/visitor-test.js +++ b/src/language/__tests__/visitor-test.js @@ -311,10 +311,10 @@ describe('Visitor', () => { expect(visited).to.deep.equal([ ['enter', 'Document', undefined, undefined], - ['enter', 'OperationDefinition', 0, undefined], + ['enter', 'OperationDefinition', 0, 'Document'], ['enter', 'Name', 'name', 'OperationDefinition'], ['leave', 'Name', 'name', 'OperationDefinition'], - ['enter', 'VariableDefinition', 0, undefined], + ['enter', 'VariableDefinition', 0, 'OperationDefinition'], ['enter', 'Variable', 'variable', 'VariableDefinition'], ['enter', 'Name', 'name', 'Variable'], ['leave', 'Name', 'name', 'Variable'], @@ -323,8 +323,8 @@ describe('Visitor', () => { ['enter', 'Name', 'name', 'NamedType'], ['leave', 'Name', 'name', 'NamedType'], ['leave', 'NamedType', 'type', 'VariableDefinition'], - ['leave', 'VariableDefinition', 0, undefined], - ['enter', 'VariableDefinition', 1, undefined], + ['leave', 'VariableDefinition', 0, 'OperationDefinition'], + ['enter', 'VariableDefinition', 1, 'OperationDefinition'], ['enter', 'Variable', 'variable', 'VariableDefinition'], ['enter', 'Name', 'name', 'Variable'], ['leave', 'Name', 'name', 'Variable'], @@ -335,160 +335,160 @@ describe('Visitor', () => { ['leave', 'NamedType', 'type', 'VariableDefinition'], ['enter', 'EnumValue', 'defaultValue', 'VariableDefinition'], ['leave', 'EnumValue', 'defaultValue', 'VariableDefinition'], - ['leave', 'VariableDefinition', 1, undefined], + ['leave', 'VariableDefinition', 1, 'OperationDefinition'], ['enter', 'SelectionSet', 'selectionSet', 'OperationDefinition'], - ['enter', 'Field', 0, undefined], + ['enter', 'Field', 0, 'SelectionSet'], ['enter', 'Name', 'alias', 'Field'], ['leave', 'Name', 'alias', 'Field'], ['enter', 'Name', 'name', 'Field'], ['leave', 'Name', 'name', 'Field'], - ['enter', 'Argument', 0, undefined], + ['enter', 'Argument', 0, 'Field'], ['enter', 'Name', 'name', 'Argument'], ['leave', 'Name', 'name', 'Argument'], ['enter', 'ListValue', 'value', 'Argument'], - ['enter', 'IntValue', 0, undefined], - ['leave', 'IntValue', 0, undefined], - ['enter', 'IntValue', 1, undefined], - ['leave', 'IntValue', 1, undefined], + ['enter', 'IntValue', 0, 'ListValue'], + ['leave', 'IntValue', 0, 'ListValue'], + ['enter', 'IntValue', 1, 'ListValue'], + ['leave', 'IntValue', 1, 'ListValue'], ['leave', 'ListValue', 'value', 'Argument'], - ['leave', 'Argument', 0, undefined], + ['leave', 'Argument', 0, 'Field'], ['enter', 'SelectionSet', 'selectionSet', 'Field'], - ['enter', 'Field', 0, undefined], + ['enter', 'Field', 0, 'SelectionSet'], ['enter', 'Name', 'name', 'Field'], ['leave', 'Name', 'name', 'Field'], - ['leave', 'Field', 0, undefined], - ['enter', 'InlineFragment', 1, undefined], + ['leave', 'Field', 0, 'SelectionSet'], + ['enter', 'InlineFragment', 1, 'SelectionSet'], ['enter', 'NamedType', 'typeCondition', 'InlineFragment'], ['enter', 'Name', 'name', 'NamedType'], ['leave', 'Name', 'name', 'NamedType'], ['leave', 'NamedType', 'typeCondition', 'InlineFragment'], - ['enter', 'Directive', 0, undefined], + ['enter', 'Directive', 0, 'InlineFragment'], ['enter', 'Name', 'name', 'Directive'], ['leave', 'Name', 'name', 'Directive'], - ['leave', 'Directive', 0, undefined], + ['leave', 'Directive', 0, 'InlineFragment'], ['enter', 'SelectionSet', 'selectionSet', 'InlineFragment'], - ['enter', 'Field', 0, undefined], + ['enter', 'Field', 0, 'SelectionSet'], ['enter', 'Name', 'name', 'Field'], ['leave', 'Name', 'name', 'Field'], ['enter', 'SelectionSet', 'selectionSet', 'Field'], - ['enter', 'Field', 0, undefined], + ['enter', 'Field', 0, 'SelectionSet'], ['enter', 'Name', 'name', 'Field'], ['leave', 'Name', 'name', 'Field'], - ['leave', 'Field', 0, undefined], - ['enter', 'Field', 1, undefined], + ['leave', 'Field', 0, 'SelectionSet'], + ['enter', 'Field', 1, 'SelectionSet'], ['enter', 'Name', 'alias', 'Field'], ['leave', 'Name', 'alias', 'Field'], ['enter', 'Name', 'name', 'Field'], ['leave', 'Name', 'name', 'Field'], - ['enter', 'Argument', 0, undefined], + ['enter', 'Argument', 0, 'Field'], ['enter', 'Name', 'name', 'Argument'], ['leave', 'Name', 'name', 'Argument'], ['enter', 'IntValue', 'value', 'Argument'], ['leave', 'IntValue', 'value', 'Argument'], - ['leave', 'Argument', 0, undefined], - ['enter', 'Argument', 1, undefined], + ['leave', 'Argument', 0, 'Field'], + ['enter', 'Argument', 1, 'Field'], ['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', 1, undefined], - ['enter', 'Directive', 0, undefined], + ['leave', 'Argument', 1, 'Field'], + ['enter', 'Directive', 0, 'Field'], ['enter', 'Name', 'name', 'Directive'], ['leave', 'Name', 'name', 'Directive'], - ['enter', 'Argument', 0, undefined], + ['enter', 'Argument', 0, 'Directive'], ['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], + ['leave', 'Argument', 0, 'Directive'], + ['leave', 'Directive', 0, 'Field'], ['enter', 'SelectionSet', 'selectionSet', 'Field'], - ['enter', 'Field', 0, undefined], + ['enter', 'Field', 0, 'SelectionSet'], ['enter', 'Name', 'name', 'Field'], ['leave', 'Name', 'name', 'Field'], - ['leave', 'Field', 0, undefined], - ['enter', 'FragmentSpread', 1, undefined], + ['leave', 'Field', 0, 'SelectionSet'], + ['enter', 'FragmentSpread', 1, 'SelectionSet'], ['enter', 'Name', 'name', 'FragmentSpread'], ['leave', 'Name', 'name', 'FragmentSpread'], - ['leave', 'FragmentSpread', 1, undefined], + ['leave', 'FragmentSpread', 1, 'SelectionSet'], ['leave', 'SelectionSet', 'selectionSet', 'Field'], - ['leave', 'Field', 1, undefined], + ['leave', 'Field', 1, 'SelectionSet'], ['leave', 'SelectionSet', 'selectionSet', 'Field'], - ['leave', 'Field', 0, undefined], + ['leave', 'Field', 0, 'SelectionSet'], ['leave', 'SelectionSet', 'selectionSet', 'InlineFragment'], - ['leave', 'InlineFragment', 1, undefined], - ['enter', 'InlineFragment', 2, undefined], - ['enter', 'Directive', 0, undefined], + ['leave', 'InlineFragment', 1, 'SelectionSet'], + ['enter', 'InlineFragment', 2, 'SelectionSet'], + ['enter', 'Directive', 0, 'InlineFragment'], ['enter', 'Name', 'name', 'Directive'], ['leave', 'Name', 'name', 'Directive'], - ['enter', 'Argument', 0, undefined], + ['enter', 'Argument', 0, 'Directive'], ['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], + ['leave', 'Argument', 0, 'Directive'], + ['leave', 'Directive', 0, 'InlineFragment'], ['enter', 'SelectionSet', 'selectionSet', 'InlineFragment'], - ['enter', 'Field', 0, undefined], + ['enter', 'Field', 0, 'SelectionSet'], ['enter', 'Name', 'name', 'Field'], ['leave', 'Name', 'name', 'Field'], - ['leave', 'Field', 0, undefined], + ['leave', 'Field', 0, 'SelectionSet'], ['leave', 'SelectionSet', 'selectionSet', 'InlineFragment'], - ['leave', 'InlineFragment', 2, undefined], - ['enter', 'InlineFragment', 3, undefined], + ['leave', 'InlineFragment', 2, 'SelectionSet'], + ['enter', 'InlineFragment', 3, 'SelectionSet'], ['enter', 'SelectionSet', 'selectionSet', 'InlineFragment'], - ['enter', 'Field', 0, undefined], + ['enter', 'Field', 0, 'SelectionSet'], ['enter', 'Name', 'name', 'Field'], ['leave', 'Name', 'name', 'Field'], - ['leave', 'Field', 0, undefined], + ['leave', 'Field', 0, 'SelectionSet'], ['leave', 'SelectionSet', 'selectionSet', 'InlineFragment'], - ['leave', 'InlineFragment', 3, undefined], + ['leave', 'InlineFragment', 3, 'SelectionSet'], ['leave', 'SelectionSet', 'selectionSet', 'Field'], - ['leave', 'Field', 0, undefined], + ['leave', 'Field', 0, 'SelectionSet'], ['leave', 'SelectionSet', 'selectionSet', 'OperationDefinition'], - ['leave', 'OperationDefinition', 0, undefined], - ['enter', 'OperationDefinition', 1, undefined], + ['leave', 'OperationDefinition', 0, 'Document'], + ['enter', 'OperationDefinition', 1, 'Document'], ['enter', 'Name', 'name', 'OperationDefinition'], ['leave', 'Name', 'name', 'OperationDefinition'], ['enter', 'SelectionSet', 'selectionSet', 'OperationDefinition'], - ['enter', 'Field', 0, undefined], + ['enter', 'Field', 0, 'SelectionSet'], ['enter', 'Name', 'name', 'Field'], ['leave', 'Name', 'name', 'Field'], - ['enter', 'Argument', 0, undefined], + ['enter', 'Argument', 0, 'Field'], ['enter', 'Name', 'name', 'Argument'], ['leave', 'Name', 'name', 'Argument'], ['enter', 'IntValue', 'value', 'Argument'], ['leave', 'IntValue', 'value', 'Argument'], - ['leave', 'Argument', 0, undefined], - ['enter', 'Directive', 0, undefined], + ['leave', 'Argument', 0, 'Field'], + ['enter', 'Directive', 0, 'Field'], ['enter', 'Name', 'name', 'Directive'], ['leave', 'Name', 'name', 'Directive'], - ['leave', 'Directive', 0, undefined], + ['leave', 'Directive', 0, 'Field'], ['enter', 'SelectionSet', 'selectionSet', 'Field'], - ['enter', 'Field', 0, undefined], + ['enter', 'Field', 0, 'SelectionSet'], ['enter', 'Name', 'name', 'Field'], ['leave', 'Name', 'name', 'Field'], ['enter', 'SelectionSet', 'selectionSet', 'Field'], - ['enter', 'Field', 0, undefined], + ['enter', 'Field', 0, 'SelectionSet'], ['enter', 'Name', 'name', 'Field'], ['leave', 'Name', 'name', 'Field'], - ['leave', 'Field', 0, undefined], + ['leave', 'Field', 0, 'SelectionSet'], ['leave', 'SelectionSet', 'selectionSet', 'Field'], - ['leave', 'Field', 0, undefined], + ['leave', 'Field', 0, 'SelectionSet'], ['leave', 'SelectionSet', 'selectionSet', 'Field'], - ['leave', 'Field', 0, undefined], + ['leave', 'Field', 0, 'SelectionSet'], ['leave', 'SelectionSet', 'selectionSet', 'OperationDefinition'], - ['leave', 'OperationDefinition', 1, undefined], - ['enter', 'OperationDefinition', 2, undefined], + ['leave', 'OperationDefinition', 1, 'Document'], + ['enter', 'OperationDefinition', 2, 'Document'], ['enter', 'Name', 'name', 'OperationDefinition'], ['leave', 'Name', 'name', 'OperationDefinition'], - ['enter', 'VariableDefinition', 0, undefined], + ['enter', 'VariableDefinition', 0, 'OperationDefinition'], ['enter', 'Variable', 'variable', 'VariableDefinition'], ['enter', 'Name', 'name', 'Variable'], ['leave', 'Name', 'name', 'Variable'], @@ -497,51 +497,51 @@ describe('Visitor', () => { ['enter', 'Name', 'name', 'NamedType'], ['leave', 'Name', 'name', 'NamedType'], ['leave', 'NamedType', 'type', 'VariableDefinition'], - ['leave', 'VariableDefinition', 0, undefined], + ['leave', 'VariableDefinition', 0, 'OperationDefinition'], ['enter', 'SelectionSet', 'selectionSet', 'OperationDefinition'], - ['enter', 'Field', 0, undefined], + ['enter', 'Field', 0, 'SelectionSet'], ['enter', 'Name', 'name', 'Field'], ['leave', 'Name', 'name', 'Field'], - ['enter', 'Argument', 0, undefined], + ['enter', 'Argument', 0, 'Field'], ['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', 'Argument', 0, 'Field'], ['enter', 'SelectionSet', 'selectionSet', 'Field'], - ['enter', 'Field', 0, undefined], + ['enter', 'Field', 0, 'SelectionSet'], ['enter', 'Name', 'name', 'Field'], ['leave', 'Name', 'name', 'Field'], ['enter', 'SelectionSet', 'selectionSet', 'Field'], - ['enter', 'Field', 0, undefined], + ['enter', 'Field', 0, 'SelectionSet'], ['enter', 'Name', 'name', 'Field'], ['leave', 'Name', 'name', 'Field'], ['enter', 'SelectionSet', 'selectionSet', 'Field'], - ['enter', 'Field', 0, undefined], + ['enter', 'Field', 0, 'SelectionSet'], ['enter', 'Name', 'name', 'Field'], ['leave', 'Name', 'name', 'Field'], - ['leave', 'Field', 0, undefined], + ['leave', 'Field', 0, 'SelectionSet'], ['leave', 'SelectionSet', 'selectionSet', 'Field'], - ['leave', 'Field', 0, undefined], - ['enter', 'Field', 1, undefined], + ['leave', 'Field', 0, 'SelectionSet'], + ['enter', 'Field', 1, 'SelectionSet'], ['enter', 'Name', 'name', 'Field'], ['leave', 'Name', 'name', 'Field'], ['enter', 'SelectionSet', 'selectionSet', 'Field'], - ['enter', 'Field', 0, undefined], + ['enter', 'Field', 0, 'SelectionSet'], ['enter', 'Name', 'name', 'Field'], ['leave', 'Name', 'name', 'Field'], - ['leave', 'Field', 0, undefined], + ['leave', 'Field', 0, 'SelectionSet'], ['leave', 'SelectionSet', 'selectionSet', 'Field'], - ['leave', 'Field', 1, undefined], + ['leave', 'Field', 1, 'SelectionSet'], ['leave', 'SelectionSet', 'selectionSet', 'Field'], - ['leave', 'Field', 0, undefined], + ['leave', 'Field', 0, 'SelectionSet'], ['leave', 'SelectionSet', 'selectionSet', 'Field'], - ['leave', 'Field', 0, undefined], + ['leave', 'Field', 0, 'SelectionSet'], ['leave', 'SelectionSet', 'selectionSet', 'OperationDefinition'], - ['leave', 'OperationDefinition', 2, undefined], - ['enter', 'FragmentDefinition', 3, undefined], + ['leave', 'OperationDefinition', 2, 'Document'], + ['enter', 'FragmentDefinition', 3, 'Document'], ['enter', 'Name', 'name', 'FragmentDefinition'], ['leave', 'Name', 'name', 'FragmentDefinition'], ['enter', 'NamedType', 'typeCondition', 'FragmentDefinition'], @@ -549,76 +549,76 @@ describe('Visitor', () => { ['leave', 'Name', 'name', 'NamedType'], ['leave', 'NamedType', 'typeCondition', 'FragmentDefinition'], ['enter', 'SelectionSet', 'selectionSet', 'FragmentDefinition'], - ['enter', 'Field', 0, undefined], + ['enter', 'Field', 0, 'SelectionSet'], ['enter', 'Name', 'name', 'Field'], ['leave', 'Name', 'name', 'Field'], - ['enter', 'Argument', 0, undefined], + ['enter', 'Argument', 0, 'Field'], ['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], - ['enter', 'Argument', 1, undefined], + ['leave', 'Argument', 0, 'Field'], + ['enter', 'Argument', 1, 'Field'], ['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', 1, undefined], - ['enter', 'Argument', 2, undefined], + ['leave', 'Argument', 1, 'Field'], + ['enter', 'Argument', 2, 'Field'], ['enter', 'Name', 'name', 'Argument'], ['leave', 'Name', 'name', 'Argument'], ['enter', 'ObjectValue', 'value', 'Argument'], - ['enter', 'ObjectField', 0, undefined], + ['enter', 'ObjectField', 0, 'ObjectValue'], ['enter', 'Name', 'name', 'ObjectField'], ['leave', 'Name', 'name', 'ObjectField'], ['enter', 'StringValue', 'value', 'ObjectField'], ['leave', 'StringValue', 'value', 'ObjectField'], - ['leave', 'ObjectField', 0, undefined], - ['enter', 'ObjectField', 1, undefined], + ['leave', 'ObjectField', 0, 'ObjectValue'], + ['enter', 'ObjectField', 1, 'ObjectValue'], ['enter', 'Name', 'name', 'ObjectField'], ['leave', 'Name', 'name', 'ObjectField'], ['enter', 'StringValue', 'value', 'ObjectField'], ['leave', 'StringValue', 'value', 'ObjectField'], - ['leave', 'ObjectField', 1, undefined], + ['leave', 'ObjectField', 1, 'ObjectValue'], ['leave', 'ObjectValue', 'value', 'Argument'], - ['leave', 'Argument', 2, undefined], - ['leave', 'Field', 0, undefined], + ['leave', 'Argument', 2, 'Field'], + ['leave', 'Field', 0, 'SelectionSet'], ['leave', 'SelectionSet', 'selectionSet', 'FragmentDefinition'], - ['leave', 'FragmentDefinition', 3, undefined], - ['enter', 'OperationDefinition', 4, undefined], + ['leave', 'FragmentDefinition', 3, 'Document'], + ['enter', 'OperationDefinition', 4, 'Document'], ['enter', 'SelectionSet', 'selectionSet', 'OperationDefinition'], - ['enter', 'Field', 0, undefined], + ['enter', 'Field', 0, 'SelectionSet'], ['enter', 'Name', 'name', 'Field'], ['leave', 'Name', 'name', 'Field'], - ['enter', 'Argument', 0, undefined], + ['enter', 'Argument', 0, 'Field'], ['enter', 'Name', 'name', 'Argument'], ['leave', 'Name', 'name', 'Argument'], ['enter', 'BooleanValue', 'value', 'Argument'], ['leave', 'BooleanValue', 'value', 'Argument'], - ['leave', 'Argument', 0, undefined], - ['enter', 'Argument', 1, undefined], + ['leave', 'Argument', 0, 'Field'], + ['enter', 'Argument', 1, 'Field'], ['enter', 'Name', 'name', 'Argument'], ['leave', 'Name', 'name', 'Argument'], ['enter', 'BooleanValue', 'value', 'Argument'], ['leave', 'BooleanValue', 'value', 'Argument'], - ['leave', 'Argument', 1, undefined], - ['enter', 'Argument', 2, undefined], + ['leave', 'Argument', 1, 'Field'], + ['enter', 'Argument', 2, 'Field'], ['enter', 'Name', 'name', 'Argument'], ['leave', 'Name', 'name', 'Argument'], ['enter', 'NullValue', 'value', 'Argument'], ['leave', 'NullValue', 'value', 'Argument'], - ['leave', 'Argument', 2, undefined], - ['leave', 'Field', 0, undefined], - ['enter', 'Field', 1, undefined], + ['leave', 'Argument', 2, 'Field'], + ['leave', 'Field', 0, 'SelectionSet'], + ['enter', 'Field', 1, 'SelectionSet'], ['enter', 'Name', 'name', 'Field'], ['leave', 'Name', 'name', 'Field'], - ['leave', 'Field', 1, undefined], + ['leave', 'Field', 1, 'SelectionSet'], ['leave', 'SelectionSet', 'selectionSet', 'OperationDefinition'], - ['leave', 'OperationDefinition', 4, undefined], + ['leave', 'OperationDefinition', 4, 'Document'], ['leave', 'Document', undefined, undefined], ]); }); diff --git a/src/language/ast.js b/src/language/ast.js index 78c1989494..dfcbe7f642 100644 --- a/src/language/ast.js +++ b/src/language/ast.js @@ -538,3 +538,56 @@ export type DirectiveDefinitionNode = { +arguments?: $ReadOnlyArray, +locations: $ReadOnlyArray, }; + +export type KindToASTNodeType = {| + Name?: NameNode, + Document?: DocumentNode, + OperationDefinition?: OperationDefinitionNode, + VariableDefinition?: VariableDefinitionNode, + Variable?: VariableNode, + SelectionSet?: SelectionSetNode, + Field?: FieldNode, + Argument?: ArgumentNode, + + FragmentSpread?: FragmentSpreadNode, + InlineFragment?: InlineFragmentNode, + FragmentDefinition?: FragmentDefinitionNode, + + IntValue?: IntValueNode, + FloatValue?: FloatValueNode, + StringValue?: StringValueNode, + BooleanValue?: BooleanValueNode, + NullValue?: NullValueNode, + EnumValue?: EnumValueNode, + ListValue?: ListValueNode, + ObjectValue?: ObjectValueNode, + ObjectField?: ObjectFieldNode, + + Directive?: DirectiveNode, + + NamedType?: NamedTypeNode, + ListType?: ListTypeNode, + NonNullType?: NonNullTypeNode, + + SchemaDefinition?: SchemaDefinitionNode, + OperationTypeDefinition?: OperationTypeDefinitionNode, + + ScalarTypeDefinition?: ScalarTypeDefinitionNode, + ObjectTypeDefinition?: ObjectTypeDefinitionNode, + FieldDefinition?: FieldDefinitionNode, + InputValueDefinition?: InputValueDefinitionNode, + InterfaceTypeDefinition?: InterfaceTypeDefinitionNode, + UnionTypeDefinition?: UnionTypeDefinitionNode, + EnumTypeDefinition?: EnumTypeDefinitionNode, + EnumValueDefinition?: EnumValueDefinitionNode, + InputObjectTypeDefinition?: InputObjectTypeDefinitionNode, + + ScalarTypeExtension?: ScalarTypeExtensionNode, + ObjectTypeExtension?: ObjectTypeExtensionNode, + InterfaceTypeExtension?: InterfaceTypeExtensionNode, + UnionTypeExtension?: UnionTypeExtensionNode, + EnumTypeExtension?: EnumTypeExtensionNode, + InputObjectTypeExtension?: InputObjectTypeExtensionNode, + + DirectiveDefinition?: DirectiveDefinitionNode, +|}; diff --git a/src/language/visitor.js b/src/language/visitor.js index f47ae59b7a..7cfc9ff0e7 100644 --- a/src/language/visitor.js +++ b/src/language/visitor.js @@ -3,11 +3,19 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. + * + * @flow */ -export const QueryDocumentKeys = { - Name: [], +import type { ASTNode, KindToASTNodeType } from './ast'; +import invariant from '../jsutils/invariant'; +type KindValues = $Keys; +type DocumentKeysType = { + [nodeName: KindValues]: $ReadOnlyArray, +}; +export const QueryDocumentKeys: DocumentKeysType = { + Name: [], Document: ['definitions'], OperationDefinition: [ 'name', @@ -76,7 +84,31 @@ export const QueryDocumentKeys = { DirectiveDefinition: ['description', 'name', 'arguments', 'locations'], }; -export const BREAK = {}; +class Break {} +export const BREAK = new Break(); + +type VisitorFn = ( + node: T, + key: ?(string | number), + parent: ?ASTNode, + path: $ReadOnlyArray, + ancestors: $ReadOnlyArray, +) => Break | false | ASTNode | mixed | void; + +type KindVisitor = {| + ...$ObjMap(T) => VisitorFn>, +|}; + +type NodeVisitor = + | VisitorFn + | {| enter?: VisitorFn, leave?: VisitorFn |}; + +type Visitor = {| + enter?: VisitorFn | KindVisitor, + leave?: VisitorFn | KindVisitor, + + ...$ObjMap(T) => NodeVisitor>, +|}; /** * visit() will walk through an AST using a depth first traversal, calling @@ -164,130 +196,140 @@ export const BREAK = {}; * } * }) */ -export function visit(root, visitor, keyMap) { +export function visit( + root: ASTNode, + visitor: Visitor, + keyMap?: DocumentKeysType, +): any { const visitorKeys = keyMap || QueryDocumentKeys; - - let stack; - let inArray = Array.isArray(root); - let keys = [root]; - let index = -1; - let edits = []; - let parent; const path = []; const ancestors = []; - let newRoot = root; - - do { - index++; - const isLeaving = index === keys.length; - let key; - let node; - const isEdited = isLeaving && edits.length !== 0; - if (isLeaving) { - key = ancestors.length === 0 ? undefined : path.pop(); - node = parent; - parent = ancestors.pop(); - if (isEdited) { - if (inArray) { - node = node.slice(); - } else { - const clone = {}; - for (const k in node) { - if (node.hasOwnProperty(k)) { - clone[k] = node[k]; - } - } - node = clone; - } - let editOffset = 0; - for (let ii = 0; ii < edits.length; ii++) { - let editKey = edits[ii][0]; - const editValue = edits[ii][1]; - if (inArray) { - editKey -= editOffset; - } - if (inArray && editValue === null) { - node.splice(editKey, 1); - editOffset++; - } else { - node[editKey] = editValue; - } + let node = root; + let parent; + let isLeaving = false; + let editStack = { depth: 0, value: {}, prev: null }; + + for (;;) { + if (!isNode(node)) { + throw new Error('Invalid AST Node: ' + JSON.stringify(node)); + } + const isInArray = typeof path[path.length - 1] === 'number'; + const curKey = path[path.length - (isInArray ? 2 : 1)]; + const curIndex = isInArray ? path[path.length - 1] : undefined; + const visitFn = getVisitFn(visitor, node.kind, isLeaving); + + if (visitFn) { + const key = path[path.length - 1]; + const result = visitFn.call(visitor, node, key, parent, path, ancestors); + if (result === BREAK) { + return root; + } else if (result === false) { + isLeaving = true; + } else if (result !== undefined) { + node = (result: any); + if (!isNode(result)) { + isLeaving = true; } } - index = stack.index; - keys = stack.keys; - edits = stack.edits; - inArray = stack.inArray; - stack = stack.prev; - } else { - key = parent ? (inArray ? index : keys[index]) : undefined; - node = parent ? parent[key] : newRoot; - if (node === null || node === undefined) { - continue; - } - if (parent) { - path.push(key); - } } - let result; - if (!Array.isArray(node)) { - if (!isNode(node)) { - throw new Error('Invalid AST Node: ' + JSON.stringify(node)); + if (!isLeaving) { + const newParent = node; + isLeaving = !switchToNextNode(node); + if (!isLeaving) { + parent = newParent; + ancestors.push(newParent); } - const visitFn = getVisitFn(visitor, node.kind, isLeaving); - if (visitFn) { - result = visitFn.call(visitor, node, key, parent, path, ancestors); + continue; + } - if (result === BREAK) { - break; - } + if (parent === undefined) { + break; + } - if (result === false) { - if (!isLeaving) { - path.pop(); - continue; - } - } else if (result !== undefined) { - edits.push([key, result]); - if (!isLeaving) { - if (isNode(result)) { - node = result; - } else { - path.pop(); - continue; - } - } - } + const oldValue = isInArray ? parent[curKey][curIndex] : parent[curKey]; + if (node !== oldValue) { + if (editStack.depth < ancestors.length) { + editStack = { depth: ancestors.length, value: {}, prev: editStack }; + } + const edits = editStack.value; + if (curIndex !== undefined) { + edits[curKey] = edits[curKey] || parent[curKey].slice(); + edits[curKey][curIndex] = node; + } else { + edits[curKey] = node; } } - if (result === undefined && isEdited) { - edits.push([key, node]); + path.pop(); + if (isInArray) { + path.pop(); + } + isLeaving = !switchToNextNode(parent, curKey, curIndex); + if (isLeaving) { + node = ancestors.pop(); + parent = ancestors[ancestors.length - 1]; + if (editStack.depth === ancestors.length + 1) { + node = applyEdits(node, editStack.value); + invariant(editStack.prev != null); + editStack = editStack.prev; + } } + } - if (!isLeaving) { - stack = { inArray, index, keys, edits, prev: stack }; - inArray = Array.isArray(node); - keys = inArray ? node : visitorKeys[node.kind] || []; - index = -1; - edits = []; - if (parent) { - ancestors.push(parent); + return node; + + function switchToNextNode(baseNode, prevKey, prevIndex) { + if (prevKey !== undefined && prevIndex !== undefined) { + const nextIndex = prevIndex + 1; + if (nextIndex < baseNode[prevKey].length) { + node = baseNode[prevKey][nextIndex]; + path.push(prevKey, nextIndex); + return true; } - parent = node; } - } while (stack !== undefined); - if (edits.length !== 0) { - newRoot = edits[edits.length - 1][1]; + const keys = visitorKeys[baseNode.kind] || []; + let keyIndex = prevKey == null ? 0 : keys.indexOf(prevKey) + 1; + for (; keyIndex < keys.length; ++keyIndex) { + const nextKey = keys[keyIndex]; + const keyValue = baseNode[nextKey]; + + if (keyValue != null) { + if (!Array.isArray(keyValue)) { + node = keyValue; + path.push(nextKey); + return true; + } else if (keyValue.length > 0) { + node = keyValue[0]; + path.push(nextKey, 0); + return true; + } + } + } + return false; } - return newRoot; + function applyEdits(oldNode, edits) { + const newNode = {}; + for (const key in oldNode) { + if (Object.prototype.hasOwnProperty.call(oldNode, key)) { + const editedValue = edits[key]; + if (editedValue === undefined) { + newNode[key] = oldNode[key]; + } else if (Array.isArray(editedValue)) { + newNode[key] = editedValue.filter(value => value !== null); + } else if (editedValue !== null) { + newNode[key] = editedValue; + } + } + } + return newNode; + } } -function isNode(maybeNode) { - return maybeNode && typeof maybeNode.kind === 'string'; +function isNode(maybeNode: mixed): boolean %checks { + return maybeNode != null && typeof maybeNode.kind === 'string'; } /** @@ -296,7 +338,7 @@ function isNode(maybeNode) { * * If a prior visitor edits a node, no following visitors will see that node. */ -export function visitInParallel(visitors) { +export function visitInParallel(visitors: $ReadOnlyArray): Visitor { const skipping = new Array(visitors.length); return { @@ -337,11 +379,15 @@ export function visitInParallel(visitors) { }; } +interface TypeInfo { enter(ASTNode): void; leave(ASTNode): void } /** * Creates a new visitor instance which maintains a provided TypeInfo instance * along with visiting visitor. */ -export function visitWithTypeInfo(typeInfo, visitor) { +export function visitWithTypeInfo( + typeInfo: TypeInfo, + visitor: Visitor, +): Visitor { return { enter(node) { typeInfo.enter(node); @@ -351,7 +397,7 @@ export function visitWithTypeInfo(typeInfo, visitor) { if (result !== undefined) { typeInfo.leave(node); if (isNode(result)) { - typeInfo.enter(result); + typeInfo.enter(((result: any): ASTNode)); } } return result; @@ -373,7 +419,11 @@ export function visitWithTypeInfo(typeInfo, visitor) { * Given a visitor instance, if it is leaving or not, and a node kind, return * the function the visitor runtime should call. */ -export function getVisitFn(visitor, kind, isLeaving) { +export function getVisitFn( + visitor: Visitor, + kind: KindValues, + isLeaving: boolean, +): VisitorFn | void { const kindVisitor = visitor[kind]; if (kindVisitor) { if (!isLeaving && typeof kindVisitor === 'function') { diff --git a/src/utilities/TypeInfo.js b/src/utilities/TypeInfo.js index d052dad0ab..9a659601d0 100644 --- a/src/utilities/TypeInfo.js +++ b/src/utilities/TypeInfo.js @@ -110,8 +110,7 @@ export class TypeInfo { return this._enumValue; } - // Flow does not yet handle this case. - enter(node: any /* ASTNode */) { + enter(node: ASTNode) { const schema = this._schema; switch (node.kind) { case Kind.SELECTION_SET: diff --git a/src/validation/rules/KnownArgumentNames.js b/src/validation/rules/KnownArgumentNames.js index 77b81a9344..2e302647f4 100644 --- a/src/validation/rules/KnownArgumentNames.js +++ b/src/validation/rules/KnownArgumentNames.js @@ -52,9 +52,9 @@ export function unknownDirectiveArgMessage( */ export function KnownArgumentNames(context: ValidationContext): any { return { - Argument(node, key, parent, path, ancestors) { - const argumentOf = ancestors[ancestors.length - 1]; - if (argumentOf.kind === Kind.FIELD) { + Argument(node, key, parent) { + invariant(parent); + if (parent.kind === Kind.FIELD) { const fieldDef = context.getFieldDef(); if (fieldDef) { const fieldArgDef = find( @@ -80,7 +80,7 @@ export function KnownArgumentNames(context: ValidationContext): any { ); } } - } else if (argumentOf.kind === Kind.DIRECTIVE) { + } else if (parent.kind === Kind.DIRECTIVE) { const directive = context.getDirective(); if (directive) { const directiveArgDef = find( diff --git a/src/validation/rules/KnownDirectives.js b/src/validation/rules/KnownDirectives.js index 1422c138d2..844eeed12d 100644 --- a/src/validation/rules/KnownDirectives.js +++ b/src/validation/rules/KnownDirectives.js @@ -109,7 +109,7 @@ function getDirectiveLocationForASTPath(ancestors) { case Kind.INPUT_OBJECT_TYPE_EXTENSION: return DirectiveLocation.INPUT_OBJECT; case Kind.INPUT_VALUE_DEFINITION: - const parentNode = ancestors[ancestors.length - 3]; + const parentNode = ancestors[ancestors.length - 2]; return parentNode.kind === Kind.INPUT_OBJECT_TYPE_DEFINITION ? DirectiveLocation.INPUT_FIELD_DEFINITION : DirectiveLocation.ARGUMENT_DEFINITION;