UNPKG

8.09 kBTypeScriptView Raw
1import { Maybe } from '../jsutils/Maybe';
2
3import { ASTNode, ASTKindToNode } from './ast';
4
5/**
6 * A visitor is provided to visit, it contains the collection of
7 * relevant functions to be called during the visitor's traversal.
8 */
9export type ASTVisitor = Visitor<ASTKindToNode>;
10export type Visitor<KindToNode, Nodes = KindToNode[keyof KindToNode]> =
11 | EnterLeaveVisitor<KindToNode, Nodes>
12 | ShapeMapVisitor<KindToNode, Nodes>;
13
14interface EnterLeave<T> {
15 readonly enter?: T;
16 readonly leave?: T;
17}
18
19type EnterLeaveVisitor<KindToNode, Nodes> = EnterLeave<
20 VisitFn<Nodes> | { [K in keyof KindToNode]?: VisitFn<Nodes, KindToNode[K]> }
21>;
22
23type ShapeMapVisitor<KindToNode, Nodes> = {
24 [K in keyof KindToNode]?:
25 | VisitFn<Nodes, KindToNode[K]>
26 | EnterLeave<VisitFn<Nodes, KindToNode[K]>>;
27};
28
29/**
30 * A visitor is comprised of visit functions, which are called on each node
31 * during the visitor's traversal.
32 */
33export type VisitFn<TAnyNode, TVisitedNode = TAnyNode> = (
34 /** The current node being visiting. */
35 node: TVisitedNode,
36 /** The index or key to this node from the parent node or Array. */
37 key: string | number | undefined,
38 /** The parent immediately above this node, which may be an Array. */
39 parent: TAnyNode | ReadonlyArray<TAnyNode> | undefined,
40 /** The key path to get to this node from the root node. */
41 path: ReadonlyArray<string | number>,
42 /**
43 * All nodes and Arrays visited before reaching parent of this node.
44 * These correspond to array indices in `path`.
45 * Note: ancestors includes arrays which contain the parent of visited node.
46 */
47 ancestors: ReadonlyArray<TAnyNode | ReadonlyArray<TAnyNode>>,
48) => any;
49
50/**
51 * A KeyMap describes each the traversable properties of each kind of node.
52 */
53export type VisitorKeyMap<T> = { [P in keyof T]: ReadonlyArray<keyof T[P]> };
54
55// TODO: Should be `[]`, but that requires TypeScript@3
56type EmptyTuple = Array<never>;
57
58export const QueryDocumentKeys: {
59 Name: EmptyTuple;
60
61 Document: ['definitions'];
62 // Prettier forces trailing commas, but TS pre 3.2 doesn't allow them.
63 // prettier-ignore
64 OperationDefinition: [
65 'name',
66 'variableDefinitions',
67 'directives',
68 'selectionSet'
69 ];
70 VariableDefinition: ['variable', 'type', 'defaultValue', 'directives'];
71 Variable: ['name'];
72 SelectionSet: ['selections'];
73 Field: ['alias', 'name', 'arguments', 'directives', 'selectionSet'];
74 Argument: ['name', 'value'];
75
76 FragmentSpread: ['name', 'directives'];
77 InlineFragment: ['typeCondition', 'directives', 'selectionSet'];
78 // prettier-ignore
79 FragmentDefinition: [
80 'name',
81 // Note: fragment variable definitions are experimental and may be changed
82 // or removed in the future.
83 'variableDefinitions',
84 'typeCondition',
85 'directives',
86 'selectionSet'
87 ];
88
89 IntValue: EmptyTuple;
90 FloatValue: EmptyTuple;
91 StringValue: EmptyTuple;
92 BooleanValue: EmptyTuple;
93 NullValue: EmptyTuple;
94 EnumValue: EmptyTuple;
95 ListValue: ['values'];
96 ObjectValue: ['fields'];
97 ObjectField: ['name', 'value'];
98
99 Directive: ['name', 'arguments'];
100
101 NamedType: ['name'];
102 ListType: ['type'];
103 NonNullType: ['type'];
104
105 SchemaDefinition: ['description', 'directives', 'operationTypes'];
106 OperationTypeDefinition: ['type'];
107
108 ScalarTypeDefinition: ['description', 'name', 'directives'];
109 // prettier-ignore
110 ObjectTypeDefinition: [
111 'description',
112 'name',
113 'interfaces',
114 'directives',
115 'fields'
116 ];
117 FieldDefinition: ['description', 'name', 'arguments', 'type', 'directives'];
118 // prettier-ignore
119 InputValueDefinition: [
120 'description',
121 'name',
122 'type',
123 'defaultValue',
124 'directives'
125 ];
126 // prettier-ignore
127 InterfaceTypeDefinition: [
128 'description',
129 'name',
130 'interfaces',
131 'directives',
132 'fields'
133 ];
134 UnionTypeDefinition: ['description', 'name', 'directives', 'types'];
135 EnumTypeDefinition: ['description', 'name', 'directives', 'values'];
136 EnumValueDefinition: ['description', 'name', 'directives'];
137 InputObjectTypeDefinition: ['description', 'name', 'directives', 'fields'];
138
139 DirectiveDefinition: ['description', 'name', 'arguments', 'locations'];
140
141 SchemaExtension: ['directives', 'operationTypes'];
142
143 ScalarTypeExtension: ['name', 'directives'];
144 ObjectTypeExtension: ['name', 'interfaces', 'directives', 'fields'];
145 InterfaceTypeExtension: ['name', 'interfaces', 'directives', 'fields'];
146 UnionTypeExtension: ['name', 'directives', 'types'];
147 EnumTypeExtension: ['name', 'directives', 'values'];
148 InputObjectTypeExtension: ['name', 'directives', 'fields'];
149};
150
151export const BREAK: any;
152
153/**
154 * visit() will walk through an AST using a depth-first traversal, calling
155 * the visitor's enter function at each node in the traversal, and calling the
156 * leave function after visiting that node and all of its child nodes.
157 *
158 * By returning different values from the enter and leave functions, the
159 * behavior of the visitor can be altered, including skipping over a sub-tree of
160 * the AST (by returning false), editing the AST by returning a value or null
161 * to remove the value, or to stop the whole traversal by returning BREAK.
162 *
163 * When using visit() to edit an AST, the original AST will not be modified, and
164 * a new version of the AST with the changes applied will be returned from the
165 * visit function.
166 *
167 * const editedAST = visit(ast, {
168 * enter(node, key, parent, path, ancestors) {
169 * // @return
170 * // undefined: no action
171 * // false: skip visiting this node
172 * // visitor.BREAK: stop visiting altogether
173 * // null: delete this node
174 * // any value: replace this node with the returned value
175 * },
176 * leave(node, key, parent, path, ancestors) {
177 * // @return
178 * // undefined: no action
179 * // false: no action
180 * // visitor.BREAK: stop visiting altogether
181 * // null: delete this node
182 * // any value: replace this node with the returned value
183 * }
184 * });
185 *
186 * Alternatively to providing enter() and leave() functions, a visitor can
187 * instead provide functions named the same as the kinds of AST nodes, or
188 * enter/leave visitors at a named key, leading to four permutations of the
189 * visitor API:
190 *
191 * 1) Named visitors triggered when entering a node of a specific kind.
192 *
193 * visit(ast, {
194 * Kind(node) {
195 * // enter the "Kind" node
196 * }
197 * })
198 *
199 * 2) Named visitors that trigger upon entering and leaving a node of
200 * a specific kind.
201 *
202 * visit(ast, {
203 * Kind: {
204 * enter(node) {
205 * // enter the "Kind" node
206 * }
207 * leave(node) {
208 * // leave the "Kind" node
209 * }
210 * }
211 * })
212 *
213 * 3) Generic visitors that trigger upon entering and leaving any node.
214 *
215 * visit(ast, {
216 * enter(node) {
217 * // enter any node
218 * },
219 * leave(node) {
220 * // leave any node
221 * }
222 * })
223 *
224 * 4) Parallel visitors for entering and leaving nodes of a specific kind.
225 *
226 * visit(ast, {
227 * enter: {
228 * Kind(node) {
229 * // enter the "Kind" node
230 * }
231 * },
232 * leave: {
233 * Kind(node) {
234 * // leave the "Kind" node
235 * }
236 * }
237 * })
238 */
239export function visit(
240 root: ASTNode,
241 visitor: Visitor<ASTKindToNode>,
242 visitorKeys?: VisitorKeyMap<ASTKindToNode>, // default: QueryDocumentKeys
243): any;
244
245/**
246 * Creates a new visitor instance which delegates to many visitors to run in
247 * parallel. Each visitor will be visited for each node before moving on.
248 *
249 * If a prior visitor edits a node, no following visitors will see that node.
250 */
251export function visitInParallel(
252 visitors: ReadonlyArray<Visitor<ASTKindToNode>>,
253): Visitor<ASTKindToNode>;
254
255/**
256 * Given a visitor instance, if it is leaving or not, and a node kind, return
257 * the function the visitor runtime should call.
258 */
259export function getVisitFn(
260 visitor: Visitor<any>,
261 kind: string,
262 isLeaving: boolean,
263): Maybe<VisitFn<any>>;