visitor.d.ts 7.9 KB

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