Skip to content

Commit

Permalink
isNode: check exact value of node's kind (#3271)
Browse files Browse the repository at this point in the history
  • Loading branch information
IvanGoncharov committed Sep 23, 2021
1 parent f4efee9 commit a05dcfc
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 89 deletions.
107 changes: 100 additions & 7 deletions src/language/ast.ts
Expand Up @@ -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.
*/
Expand Down Expand Up @@ -236,6 +229,106 @@ export interface ASTKindToNode {
InputObjectTypeExtension: InputObjectTypeExtensionNode;
}

/**
* @internal
*/
export const QueryDocumentKeys: {
[P in keyof ASTKindToNode]: ReadonlyArray<keyof ASTKindToNode[P]>;
} = {
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<string>(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 {
Expand Down
86 changes: 4 additions & 82 deletions 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';

/**
Expand Down Expand Up @@ -83,84 +84,6 @@ export type ASTVisitorKeyMap = {
[P in keyof ASTKindToNode]?: ReadonlyArray<keyof ASTKindToNode[P]>;
};

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({});

/**
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit a05dcfc

Please sign in to comment.