From 0598af4255cb3ec311f8949ed2c2080462fc18f0 Mon Sep 17 00:00:00 2001 From: Ivan Goncharov Date: Thu, 23 Sep 2021 17:41:50 +0300 Subject: [PATCH] isNode: check exact value of node's `kind` --- src/language/ast.ts | 107 +++++++++++++++++++++++++++++++++++++--- src/language/visitor.ts | 86 ++------------------------------ 2 files changed, 104 insertions(+), 89 deletions(-) diff --git a/src/language/ast.ts b/src/language/ast.ts index db0a5de83c..49c2cda584 100644 --- a/src/language/ast.ts +++ b/src/language/ast.ts @@ -132,13 +132,6 @@ export class Token { } } -/** - * @internal - */ -export function isNode(maybeNode: any): maybeNode is ASTNode { - return typeof maybeNode?.kind === 'string'; -} - /** * The list of all possible AST node types. */ @@ -236,6 +229,106 @@ export interface ASTKindToNode { InputObjectTypeExtension: InputObjectTypeExtensionNode; } +/** + * @internal + */ +export const QueryDocumentKeys: { + [P in keyof ASTKindToNode]: ReadonlyArray; +} = { + Name: [], + + Document: ['definitions'], + OperationDefinition: [ + 'name', + 'variableDefinitions', + 'directives', + 'selectionSet', + ], + VariableDefinition: ['variable', 'type', 'defaultValue', 'directives'], + Variable: ['name'], + SelectionSet: ['selections'], + Field: ['alias', 'name', 'arguments', 'directives', 'selectionSet'], + Argument: ['name', 'value'], + + FragmentSpread: ['name', 'directives'], + InlineFragment: ['typeCondition', 'directives', 'selectionSet'], + FragmentDefinition: [ + 'name', + // Note: fragment variable definitions are deprecated and will removed in v17.0.0 + 'variableDefinitions', + 'typeCondition', + 'directives', + 'selectionSet', + ], + + IntValue: [], + FloatValue: [], + StringValue: [], + BooleanValue: [], + NullValue: [], + EnumValue: [], + ListValue: ['values'], + ObjectValue: ['fields'], + ObjectField: ['name', 'value'], + + Directive: ['name', 'arguments'], + + NamedType: ['name'], + ListType: ['type'], + NonNullType: ['type'], + + SchemaDefinition: ['description', 'directives', 'operationTypes'], + OperationTypeDefinition: ['type'], + + ScalarTypeDefinition: ['description', 'name', 'directives'], + ObjectTypeDefinition: [ + 'description', + 'name', + 'interfaces', + 'directives', + 'fields', + ], + FieldDefinition: ['description', 'name', 'arguments', 'type', 'directives'], + InputValueDefinition: [ + 'description', + 'name', + 'type', + 'defaultValue', + 'directives', + ], + InterfaceTypeDefinition: [ + 'description', + 'name', + 'interfaces', + 'directives', + 'fields', + ], + UnionTypeDefinition: ['description', 'name', 'directives', 'types'], + EnumTypeDefinition: ['description', 'name', 'directives', 'values'], + EnumValueDefinition: ['description', 'name', 'directives'], + InputObjectTypeDefinition: ['description', 'name', 'directives', 'fields'], + + DirectiveDefinition: ['description', 'name', 'arguments', 'locations'], + + SchemaExtension: ['directives', 'operationTypes'], + + ScalarTypeExtension: ['name', 'directives'], + ObjectTypeExtension: ['name', 'interfaces', 'directives', 'fields'], + InterfaceTypeExtension: ['name', 'interfaces', 'directives', 'fields'], + UnionTypeExtension: ['name', 'directives', 'types'], + EnumTypeExtension: ['name', 'directives', 'values'], + InputObjectTypeExtension: ['name', 'directives', 'fields'], +}; + +const kindValues = new Set(Object.keys(QueryDocumentKeys)); +/** + * @internal + */ +export function isNode(maybeNode: any): maybeNode is ASTNode { + const maybeKind = maybeNode?.kind; + return typeof maybeKind === 'string' && kindValues.has(maybeKind); +} + /** Name */ export interface NameNode { diff --git a/src/language/visitor.ts b/src/language/visitor.ts index 0e93bcfa19..00a8c3f194 100644 --- a/src/language/visitor.ts +++ b/src/language/visitor.ts @@ -1,7 +1,8 @@ import { inspect } from '../jsutils/inspect'; +import { devAssert } from '../jsutils/devAssert'; import type { ASTNode, ASTKindToNode } from './ast'; -import { isNode } from './ast'; +import { isNode, QueryDocumentKeys } from './ast'; import { Kind } from './kinds'; /** @@ -83,84 +84,6 @@ export type ASTVisitorKeyMap = { [P in keyof ASTKindToNode]?: ReadonlyArray; }; -export const QueryDocumentKeys: ASTVisitorKeyMap = { - Document: ['definitions'], - OperationDefinition: [ - 'name', - 'variableDefinitions', - 'directives', - 'selectionSet', - ], - VariableDefinition: ['variable', 'type', 'defaultValue', 'directives'], - Variable: ['name'], - SelectionSet: ['selections'], - Field: ['alias', 'name', 'arguments', 'directives', 'selectionSet'], - Argument: ['name', 'value'], - - FragmentSpread: ['name', 'directives'], - InlineFragment: ['typeCondition', 'directives', 'selectionSet'], - FragmentDefinition: [ - 'name', - // Note: fragment variable definitions are deprecated and will removed in v17.0.0 - 'variableDefinitions', - 'typeCondition', - 'directives', - 'selectionSet', - ], - - ListValue: ['values'], - ObjectValue: ['fields'], - ObjectField: ['name', 'value'], - - Directive: ['name', 'arguments'], - - NamedType: ['name'], - ListType: ['type'], - NonNullType: ['type'], - - SchemaDefinition: ['description', 'directives', 'operationTypes'], - OperationTypeDefinition: ['type'], - - ScalarTypeDefinition: ['description', 'name', 'directives'], - ObjectTypeDefinition: [ - 'description', - 'name', - 'interfaces', - 'directives', - 'fields', - ], - FieldDefinition: ['description', 'name', 'arguments', 'type', 'directives'], - InputValueDefinition: [ - 'description', - 'name', - 'type', - 'defaultValue', - 'directives', - ], - InterfaceTypeDefinition: [ - 'description', - 'name', - 'interfaces', - 'directives', - 'fields', - ], - UnionTypeDefinition: ['description', 'name', 'directives', 'types'], - EnumTypeDefinition: ['description', 'name', 'directives', 'values'], - EnumValueDefinition: ['description', 'name', 'directives'], - InputObjectTypeDefinition: ['description', 'name', 'directives', 'fields'], - - DirectiveDefinition: ['description', 'name', 'arguments', 'locations'], - - SchemaExtension: ['directives', 'operationTypes'], - - ScalarTypeExtension: ['name', 'directives'], - ObjectTypeExtension: ['name', 'interfaces', 'directives', 'fields'], - InterfaceTypeExtension: ['name', 'interfaces', 'directives', 'fields'], - UnionTypeExtension: ['name', 'directives', 'types'], - EnumTypeExtension: ['name', 'directives', 'values'], - InputObjectTypeExtension: ['name', 'directives', 'fields'], -}; - export const BREAK: unknown = Object.freeze({}); /** @@ -324,9 +247,8 @@ export function visit( let result; if (!Array.isArray(node)) { - if (!isNode(node)) { - throw new Error(`Invalid AST Node: ${inspect(node)}.`); - } + devAssert(isNode(node), `Invalid AST Node: ${inspect(node)}.`); + const visitFn = isLeaving ? enterLeaveMap.get(node.kind)?.leave : enterLeaveMap.get(node.kind)?.enter;