Skip to content

Commit 6b0ec5e

Browse files
committed
Add Node Predicates
1 parent bce300f commit 6b0ec5e

File tree

6 files changed

+176
-86
lines changed

6 files changed

+176
-86
lines changed

src/index.js

+10
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,16 @@ export {
189189
TokenKind,
190190
DirectiveLocation,
191191
BREAK,
192+
// Predicates
193+
isDefinitionNode,
194+
isExecutableDefinitionNode,
195+
isSelectionNode,
196+
isValueNode,
197+
isTypeNode,
198+
isTypeSystemDefinitionNode,
199+
isTypeDefinitionNode,
200+
isTypeSystemExtensionNode,
201+
isTypeExtensionNode,
192202
} from './language';
193203

194204
export type {

src/language/index.js

+12
Original file line numberDiff line numberDiff line change
@@ -88,5 +88,17 @@ export type {
8888
InputObjectTypeExtensionNode,
8989
} from './ast';
9090

91+
export {
92+
isDefinitionNode,
93+
isExecutableDefinitionNode,
94+
isSelectionNode,
95+
isValueNode,
96+
isTypeNode,
97+
isTypeSystemDefinitionNode,
98+
isTypeDefinitionNode,
99+
isTypeSystemExtensionNode,
100+
isTypeExtensionNode,
101+
} from './predicates';
102+
91103
export { DirectiveLocation } from './directiveLocation';
92104
export type { DirectiveLocationEnum } from './directiveLocation';

src/language/predicates.js

+90
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/**
2+
* Copyright (c) 2018-present, Facebook, Inc.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow strict
8+
*/
9+
10+
import type { ASTNode } from './ast';
11+
import { Kind } from './kinds';
12+
13+
export function isDefinitionNode(node: ASTNode): boolean %checks {
14+
return (
15+
isExecutableDefinitionNode(node) ||
16+
isTypeSystemDefinitionNode(node) ||
17+
isTypeSystemExtensionNode(node)
18+
);
19+
}
20+
21+
export function isExecutableDefinitionNode(node: ASTNode): boolean %checks {
22+
return (
23+
node.kind === Kind.OPERATION_DEFINITION ||
24+
node.kind === Kind.FRAGMENT_DEFINITION
25+
);
26+
}
27+
28+
export function isSelectionNode(node: ASTNode): boolean %checks {
29+
return (
30+
node.kind === Kind.FIELD ||
31+
node.kind === Kind.FRAGMENT_SPREAD ||
32+
node.kind === Kind.INLINE_FRAGMENT
33+
);
34+
}
35+
36+
export function isValueNode(node: ASTNode): boolean %checks {
37+
return (
38+
node.kind === Kind.INT ||
39+
node.kind === Kind.FLOAT ||
40+
node.kind === Kind.STRING ||
41+
node.kind === Kind.BOOLEAN ||
42+
node.kind === Kind.NULL ||
43+
node.kind === Kind.ENUM ||
44+
node.kind === Kind.LIST ||
45+
node.kind === Kind.OBJECT ||
46+
node.kind === Kind.OBJECT_FIELD
47+
);
48+
}
49+
50+
export function isTypeNode(node: ASTNode): boolean %checks {
51+
return (
52+
node.kind === Kind.NAMED_TYPE ||
53+
node.kind === Kind.LIST_TYPE ||
54+
node.kind === Kind.NON_NULL_TYPE
55+
);
56+
}
57+
58+
export function isTypeSystemDefinitionNode(node: ASTNode): boolean %checks {
59+
return (
60+
node.kind === Kind.SCHEMA_DEFINITION ||
61+
isTypeDefinitionNode(node) ||
62+
node.kind === Kind.DIRECTIVE_DEFINITION
63+
);
64+
}
65+
66+
export function isTypeDefinitionNode(node: ASTNode): boolean %checks {
67+
return (
68+
node.kind === Kind.SCALAR_TYPE_DEFINITION ||
69+
node.kind === Kind.OBJECT_TYPE_DEFINITION ||
70+
node.kind === Kind.INTERFACE_TYPE_DEFINITION ||
71+
node.kind === Kind.UNION_TYPE_DEFINITION ||
72+
node.kind === Kind.ENUM_TYPE_DEFINITION ||
73+
node.kind === Kind.INPUT_OBJECT_TYPE_DEFINITION
74+
);
75+
}
76+
77+
export function isTypeSystemExtensionNode(node: ASTNode): boolean %checks {
78+
return node.kind === Kind.SCHEMA_EXTENSION || isTypeExtensionNode(node);
79+
}
80+
81+
export function isTypeExtensionNode(node: ASTNode): boolean %checks {
82+
return (
83+
node.kind === Kind.SCALAR_TYPE_EXTENSION ||
84+
node.kind === Kind.OBJECT_TYPE_EXTENSION ||
85+
node.kind === Kind.INTERFACE_TYPE_EXTENSION ||
86+
node.kind === Kind.UNION_TYPE_EXTENSION ||
87+
node.kind === Kind.ENUM_TYPE_EXTENSION ||
88+
node.kind === Kind.INPUT_OBJECT_TYPE_EXTENSION
89+
);
90+
}

src/utilities/buildASTSchema.js

+13-21
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import type {
4040
StringValueNode,
4141
Location,
4242
} from '../language/ast';
43+
import { isTypeDefinitionNode } from '../language/predicates';
4344

4445
import type { DirectiveLocationEnum } from '../language/directiveLocation';
4546

@@ -132,27 +133,18 @@ export function buildASTSchema(
132133
const nodeMap: ObjMap<TypeDefinitionNode> = Object.create(null);
133134
const directiveDefs: Array<DirectiveDefinitionNode> = [];
134135
for (let i = 0; i < documentAST.definitions.length; i++) {
135-
const d = documentAST.definitions[i];
136-
switch (d.kind) {
137-
case Kind.SCHEMA_DEFINITION:
138-
schemaDef = d;
139-
break;
140-
case Kind.SCALAR_TYPE_DEFINITION:
141-
case Kind.OBJECT_TYPE_DEFINITION:
142-
case Kind.INTERFACE_TYPE_DEFINITION:
143-
case Kind.ENUM_TYPE_DEFINITION:
144-
case Kind.UNION_TYPE_DEFINITION:
145-
case Kind.INPUT_OBJECT_TYPE_DEFINITION:
146-
const typeName = d.name.value;
147-
if (nodeMap[typeName]) {
148-
throw new Error(`Type "${typeName}" was defined more than once.`);
149-
}
150-
typeDefs.push(d);
151-
nodeMap[typeName] = d;
152-
break;
153-
case Kind.DIRECTIVE_DEFINITION:
154-
directiveDefs.push(d);
155-
break;
136+
const def = documentAST.definitions[i];
137+
if (def.kind === Kind.SCHEMA_DEFINITION) {
138+
schemaDef = def;
139+
} else if (isTypeDefinitionNode(def)) {
140+
const typeName = def.name.value;
141+
if (nodeMap[typeName]) {
142+
throw new Error(`Type "${typeName}" was defined more than once.`);
143+
}
144+
typeDefs.push(def);
145+
nodeMap[typeName] = def;
146+
} else if (def.kind === Kind.DIRECTIVE_DEFINITION) {
147+
directiveDefs.push(def);
156148
}
157149
}
158150

src/utilities/extendSchema.js

+49-61
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ import type {
5656
SchemaExtensionNode,
5757
SchemaDefinitionNode,
5858
} from '../language/ast';
59+
import {
60+
isTypeDefinitionNode,
61+
isTypeExtensionNode,
62+
} from '../language/predicates';
5963

6064
type Options = {|
6165
...GraphQLSchemaValidationOptions,
@@ -125,67 +129,51 @@ export function extendSchema(
125129

126130
for (let i = 0; i < documentAST.definitions.length; i++) {
127131
const def = documentAST.definitions[i];
128-
switch (def.kind) {
129-
case Kind.SCHEMA_DEFINITION:
130-
schemaDef = def;
131-
break;
132-
case Kind.SCHEMA_EXTENSION:
133-
schemaExtensions.push(def);
134-
break;
135-
case Kind.OBJECT_TYPE_DEFINITION:
136-
case Kind.INTERFACE_TYPE_DEFINITION:
137-
case Kind.ENUM_TYPE_DEFINITION:
138-
case Kind.UNION_TYPE_DEFINITION:
139-
case Kind.SCALAR_TYPE_DEFINITION:
140-
case Kind.INPUT_OBJECT_TYPE_DEFINITION:
141-
// Sanity check that none of the defined types conflict with the
142-
// schema's existing types.
143-
const typeName = def.name.value;
144-
if (schema.getType(typeName)) {
145-
throw new GraphQLError(
146-
`Type "${typeName}" already exists in the schema. It cannot also ` +
147-
'be defined in this type definition.',
148-
[def],
149-
);
150-
}
151-
typeDefinitionMap[typeName] = def;
152-
break;
153-
case Kind.SCALAR_TYPE_EXTENSION:
154-
case Kind.OBJECT_TYPE_EXTENSION:
155-
case Kind.INTERFACE_TYPE_EXTENSION:
156-
case Kind.ENUM_TYPE_EXTENSION:
157-
case Kind.INPUT_OBJECT_TYPE_EXTENSION:
158-
case Kind.UNION_TYPE_EXTENSION:
159-
// Sanity check that this type extension exists within the
160-
// schema's existing types.
161-
const extendedTypeName = def.name.value;
162-
const existingType = schema.getType(extendedTypeName);
163-
if (!existingType) {
164-
throw new GraphQLError(
165-
`Cannot extend type "${extendedTypeName}" because it does not ` +
166-
'exist in the existing schema.',
167-
[def],
168-
);
169-
}
170-
checkExtensionNode(existingType, def);
171-
172-
const existingTypeExtensions = typeExtensionsMap[extendedTypeName];
173-
typeExtensionsMap[extendedTypeName] = existingTypeExtensions
174-
? existingTypeExtensions.concat([def])
175-
: [def];
176-
break;
177-
case Kind.DIRECTIVE_DEFINITION:
178-
const directiveName = def.name.value;
179-
const existingDirective = schema.getDirective(directiveName);
180-
if (existingDirective) {
181-
throw new GraphQLError(
182-
`Directive "${directiveName}" already exists in the schema. It ` +
183-
'cannot be redefined.',
184-
[def],
185-
);
186-
}
187-
directiveDefinitions.push(def);
188-
break;
132+
if (def.kind === Kind.SCHEMA_DEFINITION) {
133+
schemaDef = def;
134+
} else if (def.kind === Kind.SCHEMA_EXTENSION) {
135+
schemaExtensions.push(def);
136+
} else if (isTypeDefinitionNode(def)) {
137+
// Sanity check that none of the defined types conflict with the
138+
// schema's existing types.
139+
const typeName = def.name.value;
140+
if (schema.getType(typeName)) {
141+
throw new GraphQLError(
142+
`Type "${typeName}" already exists in the schema. It cannot also ` +
143+
'be defined in this type definition.',
144+
[def],
145+
);
146+
}
147+
typeDefinitionMap[typeName] = def;
148+
} else if (isTypeExtensionNode(def)) {
149+
// Sanity check that this type extension exists within the
150+
// schema's existing types.
151+
const extendedTypeName = def.name.value;
152+
const existingType = schema.getType(extendedTypeName);
153+
if (!existingType) {
154+
throw new GraphQLError(
155+
`Cannot extend type "${extendedTypeName}" because it does not ` +
156+
'exist in the existing schema.',
157+
[def],
158+
);
159+
}
160+
checkExtensionNode(existingType, def);
161+
162+
const existingTypeExtensions = typeExtensionsMap[extendedTypeName];
163+
typeExtensionsMap[extendedTypeName] = existingTypeExtensions
164+
? existingTypeExtensions.concat([def])
165+
: [def];
166+
} else if (def.kind === Kind.DIRECTIVE_DEFINITION) {
167+
const directiveName = def.name.value;
168+
const existingDirective = schema.getDirective(directiveName);
169+
if (existingDirective) {
170+
throw new GraphQLError(
171+
`Directive "${directiveName}" already exists in the schema. It ` +
172+
'cannot be redefined.',
173+
[def],
174+
);
175+
}
176+
directiveDefinitions.push(def);
189177
}
190178
}
191179

src/validation/rules/ExecutableDefinitions.js

+2-4
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import type { ASTValidationContext } from '../ValidationContext';
1111
import { GraphQLError } from '../../error';
1212
import { Kind } from '../../language/kinds';
13+
import { isExecutableDefinitionNode } from '../../language/predicates';
1314
import type { ASTVisitor } from '../../language/visitor';
1415

1516
export function nonExecutableDefinitionMessage(defName: string): string {
@@ -28,10 +29,7 @@ export function ExecutableDefinitions(
2829
return {
2930
Document(node) {
3031
for (const definition of node.definitions) {
31-
if (
32-
definition.kind !== Kind.OPERATION_DEFINITION &&
33-
definition.kind !== Kind.FRAGMENT_DEFINITION
34-
) {
32+
if (!isExecutableDefinitionNode(node)) {
3533
context.reportError(
3634
new GraphQLError(
3735
nonExecutableDefinitionMessage(

0 commit comments

Comments
 (0)