Skip to content

Commit a05dcfc

Browse files
isNode: check exact value of node's kind (#3271)
1 parent f4efee9 commit a05dcfc

File tree

2 files changed

+104
-89
lines changed

2 files changed

+104
-89
lines changed

src/language/ast.ts

Lines changed: 100 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -132,13 +132,6 @@ export class Token {
132132
}
133133
}
134134

135-
/**
136-
* @internal
137-
*/
138-
export function isNode(maybeNode: any): maybeNode is ASTNode {
139-
return typeof maybeNode?.kind === 'string';
140-
}
141-
142135
/**
143136
* The list of all possible AST node types.
144137
*/
@@ -236,6 +229,106 @@ export interface ASTKindToNode {
236229
InputObjectTypeExtension: InputObjectTypeExtensionNode;
237230
}
238231

232+
/**
233+
* @internal
234+
*/
235+
export const QueryDocumentKeys: {
236+
[P in keyof ASTKindToNode]: ReadonlyArray<keyof ASTKindToNode[P]>;
237+
} = {
238+
Name: [],
239+
240+
Document: ['definitions'],
241+
OperationDefinition: [
242+
'name',
243+
'variableDefinitions',
244+
'directives',
245+
'selectionSet',
246+
],
247+
VariableDefinition: ['variable', 'type', 'defaultValue', 'directives'],
248+
Variable: ['name'],
249+
SelectionSet: ['selections'],
250+
Field: ['alias', 'name', 'arguments', 'directives', 'selectionSet'],
251+
Argument: ['name', 'value'],
252+
253+
FragmentSpread: ['name', 'directives'],
254+
InlineFragment: ['typeCondition', 'directives', 'selectionSet'],
255+
FragmentDefinition: [
256+
'name',
257+
// Note: fragment variable definitions are deprecated and will removed in v17.0.0
258+
'variableDefinitions',
259+
'typeCondition',
260+
'directives',
261+
'selectionSet',
262+
],
263+
264+
IntValue: [],
265+
FloatValue: [],
266+
StringValue: [],
267+
BooleanValue: [],
268+
NullValue: [],
269+
EnumValue: [],
270+
ListValue: ['values'],
271+
ObjectValue: ['fields'],
272+
ObjectField: ['name', 'value'],
273+
274+
Directive: ['name', 'arguments'],
275+
276+
NamedType: ['name'],
277+
ListType: ['type'],
278+
NonNullType: ['type'],
279+
280+
SchemaDefinition: ['description', 'directives', 'operationTypes'],
281+
OperationTypeDefinition: ['type'],
282+
283+
ScalarTypeDefinition: ['description', 'name', 'directives'],
284+
ObjectTypeDefinition: [
285+
'description',
286+
'name',
287+
'interfaces',
288+
'directives',
289+
'fields',
290+
],
291+
FieldDefinition: ['description', 'name', 'arguments', 'type', 'directives'],
292+
InputValueDefinition: [
293+
'description',
294+
'name',
295+
'type',
296+
'defaultValue',
297+
'directives',
298+
],
299+
InterfaceTypeDefinition: [
300+
'description',
301+
'name',
302+
'interfaces',
303+
'directives',
304+
'fields',
305+
],
306+
UnionTypeDefinition: ['description', 'name', 'directives', 'types'],
307+
EnumTypeDefinition: ['description', 'name', 'directives', 'values'],
308+
EnumValueDefinition: ['description', 'name', 'directives'],
309+
InputObjectTypeDefinition: ['description', 'name', 'directives', 'fields'],
310+
311+
DirectiveDefinition: ['description', 'name', 'arguments', 'locations'],
312+
313+
SchemaExtension: ['directives', 'operationTypes'],
314+
315+
ScalarTypeExtension: ['name', 'directives'],
316+
ObjectTypeExtension: ['name', 'interfaces', 'directives', 'fields'],
317+
InterfaceTypeExtension: ['name', 'interfaces', 'directives', 'fields'],
318+
UnionTypeExtension: ['name', 'directives', 'types'],
319+
EnumTypeExtension: ['name', 'directives', 'values'],
320+
InputObjectTypeExtension: ['name', 'directives', 'fields'],
321+
};
322+
323+
const kindValues = new Set<string>(Object.keys(QueryDocumentKeys));
324+
/**
325+
* @internal
326+
*/
327+
export function isNode(maybeNode: any): maybeNode is ASTNode {
328+
const maybeKind = maybeNode?.kind;
329+
return typeof maybeKind === 'string' && kindValues.has(maybeKind);
330+
}
331+
239332
/** Name */
240333

241334
export interface NameNode {

src/language/visitor.ts

Lines changed: 4 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { inspect } from '../jsutils/inspect';
2+
import { devAssert } from '../jsutils/devAssert';
23

34
import type { ASTNode, ASTKindToNode } from './ast';
4-
import { isNode } from './ast';
5+
import { isNode, QueryDocumentKeys } from './ast';
56
import { Kind } from './kinds';
67

78
/**
@@ -83,84 +84,6 @@ export type ASTVisitorKeyMap = {
8384
[P in keyof ASTKindToNode]?: ReadonlyArray<keyof ASTKindToNode[P]>;
8485
};
8586

86-
export const QueryDocumentKeys: ASTVisitorKeyMap = {
87-
Document: ['definitions'],
88-
OperationDefinition: [
89-
'name',
90-
'variableDefinitions',
91-
'directives',
92-
'selectionSet',
93-
],
94-
VariableDefinition: ['variable', 'type', 'defaultValue', 'directives'],
95-
Variable: ['name'],
96-
SelectionSet: ['selections'],
97-
Field: ['alias', 'name', 'arguments', 'directives', 'selectionSet'],
98-
Argument: ['name', 'value'],
99-
100-
FragmentSpread: ['name', 'directives'],
101-
InlineFragment: ['typeCondition', 'directives', 'selectionSet'],
102-
FragmentDefinition: [
103-
'name',
104-
// Note: fragment variable definitions are deprecated and will removed in v17.0.0
105-
'variableDefinitions',
106-
'typeCondition',
107-
'directives',
108-
'selectionSet',
109-
],
110-
111-
ListValue: ['values'],
112-
ObjectValue: ['fields'],
113-
ObjectField: ['name', 'value'],
114-
115-
Directive: ['name', 'arguments'],
116-
117-
NamedType: ['name'],
118-
ListType: ['type'],
119-
NonNullType: ['type'],
120-
121-
SchemaDefinition: ['description', 'directives', 'operationTypes'],
122-
OperationTypeDefinition: ['type'],
123-
124-
ScalarTypeDefinition: ['description', 'name', 'directives'],
125-
ObjectTypeDefinition: [
126-
'description',
127-
'name',
128-
'interfaces',
129-
'directives',
130-
'fields',
131-
],
132-
FieldDefinition: ['description', 'name', 'arguments', 'type', 'directives'],
133-
InputValueDefinition: [
134-
'description',
135-
'name',
136-
'type',
137-
'defaultValue',
138-
'directives',
139-
],
140-
InterfaceTypeDefinition: [
141-
'description',
142-
'name',
143-
'interfaces',
144-
'directives',
145-
'fields',
146-
],
147-
UnionTypeDefinition: ['description', 'name', 'directives', 'types'],
148-
EnumTypeDefinition: ['description', 'name', 'directives', 'values'],
149-
EnumValueDefinition: ['description', 'name', 'directives'],
150-
InputObjectTypeDefinition: ['description', 'name', 'directives', 'fields'],
151-
152-
DirectiveDefinition: ['description', 'name', 'arguments', 'locations'],
153-
154-
SchemaExtension: ['directives', 'operationTypes'],
155-
156-
ScalarTypeExtension: ['name', 'directives'],
157-
ObjectTypeExtension: ['name', 'interfaces', 'directives', 'fields'],
158-
InterfaceTypeExtension: ['name', 'interfaces', 'directives', 'fields'],
159-
UnionTypeExtension: ['name', 'directives', 'types'],
160-
EnumTypeExtension: ['name', 'directives', 'values'],
161-
InputObjectTypeExtension: ['name', 'directives', 'fields'],
162-
};
163-
16487
export const BREAK: unknown = Object.freeze({});
16588

16689
/**
@@ -324,9 +247,8 @@ export function visit(
324247

325248
let result;
326249
if (!Array.isArray(node)) {
327-
if (!isNode(node)) {
328-
throw new Error(`Invalid AST Node: ${inspect(node)}.`);
329-
}
250+
devAssert(isNode(node), `Invalid AST Node: ${inspect(node)}.`);
251+
330252
const visitFn = isLeaving
331253
? enterLeaveMap.get(node.kind)?.leave
332254
: enterLeaveMap.get(node.kind)?.enter;

0 commit comments

Comments
 (0)