visitor.d.ts 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. import Maybe from '../tsutils/Maybe';
  2. import { TypeInfo } from '../utilities/TypeInfo';
  3. import { ASTNode, ASTKindToNode } from './ast';
  4. /**
  5. * A visitor is provided to visit, it contains the collection of
  6. * relevant functions to be called during the visitor's traversal.
  7. */
  8. export type ASTVisitor = Visitor<ASTKindToNode>;
  9. export type Visitor<KindToNode, Nodes = KindToNode[keyof KindToNode]> =
  10. | EnterLeaveVisitor<KindToNode, Nodes>
  11. | ShapeMapVisitor<KindToNode, Nodes>;
  12. interface EnterLeave<T> {
  13. readonly enter?: T;
  14. readonly leave?: T;
  15. }
  16. type EnterLeaveVisitor<KindToNode, Nodes> = EnterLeave<
  17. VisitFn<Nodes> | { [K in keyof KindToNode]?: VisitFn<Nodes, KindToNode[K]> }
  18. >;
  19. type ShapeMapVisitor<KindToNode, Nodes> = {
  20. [K in keyof KindToNode]?:
  21. | VisitFn<Nodes, KindToNode[K]>
  22. | EnterLeave<VisitFn<Nodes, KindToNode[K]>>;
  23. };
  24. /**
  25. * A visitor is comprised of visit functions, which are called on each node
  26. * during the visitor's traversal.
  27. */
  28. export type VisitFn<TAnyNode, TVisitedNode = TAnyNode> = (
  29. /** The current node being visiting.*/
  30. node: TVisitedNode,
  31. /** The index or key to this node from the parent node or Array. */
  32. key: string | number | undefined,
  33. /** The parent immediately above this node, which may be an Array. */
  34. parent: TAnyNode | ReadonlyArray<TAnyNode> | undefined,
  35. /** The key path to get to this node from the root node. */
  36. path: ReadonlyArray<string | number>,
  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 = 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: ['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. InterfaceTypeDefinition: ['description', 'name', 'directives', 'fields'];
  111. UnionTypeDefinition: ['description', 'name', 'directives', 'types'];
  112. EnumTypeDefinition: ['description', 'name', 'directives', 'values'];
  113. EnumValueDefinition: ['description', 'name', 'directives'];
  114. InputObjectTypeDefinition: ['description', 'name', 'directives', 'fields'];
  115. DirectiveDefinition: ['description', 'name', 'arguments', 'locations'];
  116. SchemaExtension: ['directives', 'operationTypes'];
  117. ScalarTypeExtension: ['name', 'directives'];
  118. ObjectTypeExtension: ['name', 'interfaces', 'directives', 'fields'];
  119. InterfaceTypeExtension: ['name', 'directives', 'fields'];
  120. UnionTypeExtension: ['name', 'directives', 'types'];
  121. EnumTypeExtension: ['name', 'directives', 'values'];
  122. InputObjectTypeExtension: ['name', 'directives', 'fields'];
  123. };
  124. export const BREAK: any;
  125. /**
  126. * visit() will walk through an AST using a depth first traversal, calling
  127. * the visitor's enter function at each node in the traversal, and calling the
  128. * leave function after visiting that node and all of its child nodes.
  129. *
  130. * By returning different values from the enter and leave functions, the
  131. * behavior of the visitor can be altered, including skipping over a sub-tree of
  132. * the AST (by returning false), editing the AST by returning a value or null
  133. * to remove the value, or to stop the whole traversal by returning BREAK.
  134. *
  135. * When using visit() to edit an AST, the original AST will not be modified, and
  136. * a new version of the AST with the changes applied will be returned from the
  137. * visit function.
  138. *
  139. * const editedAST = visit(ast, {
  140. * enter(node, key, parent, path, ancestors) {
  141. * // @return
  142. * // undefined: no action
  143. * // false: skip visiting this node
  144. * // visitor.BREAK: stop visiting altogether
  145. * // null: delete this node
  146. * // any value: replace this node with the returned value
  147. * },
  148. * leave(node, key, parent, path, ancestors) {
  149. * // @return
  150. * // undefined: no action
  151. * // false: no action
  152. * // visitor.BREAK: stop visiting altogether
  153. * // null: delete this node
  154. * // any value: replace this node with the returned value
  155. * }
  156. * });
  157. *
  158. * Alternatively to providing enter() and leave() functions, a visitor can
  159. * instead provide functions named the same as the kinds of AST nodes, or
  160. * enter/leave visitors at a named key, leading to four permutations of
  161. * visitor API:
  162. *
  163. * 1) Named visitors triggered when entering a node a specific kind.
  164. *
  165. * visit(ast, {
  166. * Kind(node) {
  167. * // enter the "Kind" node
  168. * }
  169. * })
  170. *
  171. * 2) Named visitors that trigger upon entering and leaving a node of
  172. * a specific kind.
  173. *
  174. * visit(ast, {
  175. * Kind: {
  176. * enter(node) {
  177. * // enter the "Kind" node
  178. * }
  179. * leave(node) {
  180. * // leave the "Kind" node
  181. * }
  182. * }
  183. * })
  184. *
  185. * 3) Generic visitors that trigger upon entering and leaving any node.
  186. *
  187. * visit(ast, {
  188. * enter(node) {
  189. * // enter any node
  190. * },
  191. * leave(node) {
  192. * // leave any node
  193. * }
  194. * })
  195. *
  196. * 4) Parallel visitors for entering and leaving nodes of a specific kind.
  197. *
  198. * visit(ast, {
  199. * enter: {
  200. * Kind(node) {
  201. * // enter the "Kind" node
  202. * }
  203. * },
  204. * leave: {
  205. * Kind(node) {
  206. * // leave the "Kind" node
  207. * }
  208. * }
  209. * })
  210. */
  211. export function visit(
  212. root: ASTNode,
  213. visitor: Visitor<ASTKindToNode>,
  214. visitorKeys?: VisitorKeyMap<ASTKindToNode>, // default: QueryDocumentKeys
  215. ): any;
  216. /**
  217. * Creates a new visitor instance which delegates to many visitors to run in
  218. * parallel. Each visitor will be visited for each node before moving on.
  219. *
  220. * If a prior visitor edits a node, no following visitors will see that node.
  221. */
  222. export function visitInParallel(
  223. visitors: ReadonlyArray<Visitor<ASTKindToNode>>,
  224. ): Visitor<ASTKindToNode>;
  225. /**
  226. * Creates a new visitor instance which maintains a provided TypeInfo instance
  227. * along with visiting visitor.
  228. */
  229. export function visitWithTypeInfo(
  230. typeInfo: TypeInfo,
  231. visitor: Visitor<ASTKindToNode>,
  232. ): Visitor<ASTKindToNode>;
  233. /**
  234. * Given a visitor instance, if it is leaving or not, and a node kind, return
  235. * the function the visitor runtime should call.
  236. */
  237. export function getVisitFn(
  238. visitor: Visitor<any>,
  239. kind: string,
  240. isLeaving: boolean,
  241. ): Maybe<VisitFn<any>>;