index.js 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. /**
  2. * @typedef {import('unist').Node} UnistNode
  3. * @typedef {import('unist').Parent} UnistParent
  4. * @typedef {import('unist-util-visit-parents').VisitorResult} VisitorResult
  5. */
  6. /**
  7. * @typedef {Exclude<import('unist-util-is').Test, undefined> | undefined} Test
  8. * Test from `unist-util-is`.
  9. *
  10. * Note: we have remove and add `undefined`, because otherwise when generating
  11. * automatic `.d.ts` files, TS tries to flatten paths from a local perspective,
  12. * which doesn’t work when publishing on npm.
  13. */
  14. // To do: use types from `unist-util-visit-parents` when it’s released.
  15. /**
  16. * @typedef {(
  17. * Fn extends (value: any) => value is infer Thing
  18. * ? Thing
  19. * : Fallback
  20. * )} Predicate
  21. * Get the value of a type guard `Fn`.
  22. * @template Fn
  23. * Value; typically function that is a type guard (such as `(x): x is Y`).
  24. * @template Fallback
  25. * Value to yield if `Fn` is not a type guard.
  26. */
  27. /**
  28. * @typedef {(
  29. * Check extends null | undefined // No test.
  30. * ? Value
  31. * : Value extends {type: Check} // String (type) test.
  32. * ? Value
  33. * : Value extends Check // Partial test.
  34. * ? Value
  35. * : Check extends Function // Function test.
  36. * ? Predicate<Check, Value> extends Value
  37. * ? Predicate<Check, Value>
  38. * : never
  39. * : never // Some other test?
  40. * )} MatchesOne
  41. * Check whether a node matches a primitive check in the type system.
  42. * @template Value
  43. * Value; typically unist `Node`.
  44. * @template Check
  45. * Value; typically `unist-util-is`-compatible test, but not arrays.
  46. */
  47. /**
  48. * @typedef {(
  49. * Check extends Array<any>
  50. * ? MatchesOne<Value, Check[keyof Check]>
  51. * : MatchesOne<Value, Check>
  52. * )} Matches
  53. * Check whether a node matches a check in the type system.
  54. * @template Value
  55. * Value; typically unist `Node`.
  56. * @template Check
  57. * Value; typically `unist-util-is`-compatible test.
  58. */
  59. /**
  60. * @typedef {0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10} Uint
  61. * Number; capped reasonably.
  62. */
  63. /**
  64. * @typedef {I extends 0 ? 1 : I extends 1 ? 2 : I extends 2 ? 3 : I extends 3 ? 4 : I extends 4 ? 5 : I extends 5 ? 6 : I extends 6 ? 7 : I extends 7 ? 8 : I extends 8 ? 9 : 10} Increment
  65. * Increment a number in the type system.
  66. * @template {Uint} [I=0]
  67. * Index.
  68. */
  69. /**
  70. * @typedef {(
  71. * Node extends UnistParent
  72. * ? Node extends {children: Array<infer Children>}
  73. * ? Child extends Children ? Node : never
  74. * : never
  75. * : never
  76. * )} InternalParent
  77. * Collect nodes that can be parents of `Child`.
  78. * @template {UnistNode} Node
  79. * All node types in a tree.
  80. * @template {UnistNode} Child
  81. * Node to search for.
  82. */
  83. /**
  84. * @typedef {InternalParent<InclusiveDescendant<Tree>, Child>} Parent
  85. * Collect nodes in `Tree` that can be parents of `Child`.
  86. * @template {UnistNode} Tree
  87. * All node types in a tree.
  88. * @template {UnistNode} Child
  89. * Node to search for.
  90. */
  91. /**
  92. * @typedef {(
  93. * Depth extends Max
  94. * ? never
  95. * :
  96. * | InternalParent<Node, Child>
  97. * | InternalAncestor<Node, InternalParent<Node, Child>, Max, Increment<Depth>>
  98. * )} InternalAncestor
  99. * Collect nodes in `Tree` that can be ancestors of `Child`.
  100. * @template {UnistNode} Node
  101. * All node types in a tree.
  102. * @template {UnistNode} Child
  103. * Node to search for.
  104. * @template {Uint} [Max=10]
  105. * Max; searches up to this depth.
  106. * @template {Uint} [Depth=0]
  107. * Current depth.
  108. */
  109. /**
  110. * @typedef {(
  111. * Tree extends UnistParent
  112. * ? Depth extends Max
  113. * ? Tree
  114. * : Tree | InclusiveDescendant<Tree['children'][number], Max, Increment<Depth>>
  115. * : Tree
  116. * )} InclusiveDescendant
  117. * Collect all (inclusive) descendants of `Tree`.
  118. *
  119. * > 👉 **Note**: for performance reasons, this seems to be the fastest way to
  120. * > recurse without actually running into an infinite loop, which the
  121. * > previous version did.
  122. * >
  123. * > Practically, a max of `2` is typically enough assuming a `Root` is
  124. * > passed, but it doesn’t improve performance.
  125. * > It gets higher with `List > ListItem > Table > TableRow > TableCell`.
  126. * > Using up to `10` doesn’t hurt or help either.
  127. * @template {UnistNode} Tree
  128. * Tree type.
  129. * @template {Uint} [Max=10]
  130. * Max; searches up to this depth.
  131. * @template {Uint} [Depth=0]
  132. * Current depth.
  133. */
  134. /**
  135. * @callback Visitor
  136. * Handle a node (matching `test`, if given).
  137. *
  138. * Visitors are free to transform `node`.
  139. * They can also transform `parent`.
  140. *
  141. * Replacing `node` itself, if `SKIP` is not returned, still causes its
  142. * descendants to be walked (which is a bug).
  143. *
  144. * When adding or removing previous siblings of `node` (or next siblings, in
  145. * case of reverse), the `Visitor` should return a new `Index` to specify the
  146. * sibling to traverse after `node` is traversed.
  147. * Adding or removing next siblings of `node` (or previous siblings, in case
  148. * of reverse) is handled as expected without needing to return a new `Index`.
  149. *
  150. * Removing the children property of `parent` still results in them being
  151. * traversed.
  152. * @param {Visited} node
  153. * Found node.
  154. * @param {Visited extends UnistNode ? number | undefined : never} index
  155. * Index of `node` in `parent`.
  156. * @param {Ancestor extends UnistParent ? Ancestor | undefined : never} parent
  157. * Parent of `node`.
  158. * @returns {VisitorResult}
  159. * What to do next.
  160. *
  161. * An `Index` is treated as a tuple of `[CONTINUE, Index]`.
  162. * An `Action` is treated as a tuple of `[Action]`.
  163. *
  164. * Passing a tuple back only makes sense if the `Action` is `SKIP`.
  165. * When the `Action` is `EXIT`, that action can be returned.
  166. * When the `Action` is `CONTINUE`, `Index` can be returned.
  167. * @template {UnistNode} [Visited=UnistNode]
  168. * Visited node type.
  169. * @template {UnistParent} [Ancestor=UnistParent]
  170. * Ancestor type.
  171. */
  172. /**
  173. * @typedef {Visitor<Visited, Parent<Ancestor, Visited>>} BuildVisitorFromMatch
  174. * Build a typed `Visitor` function from a node and all possible parents.
  175. *
  176. * It will infer which values are passed as `node` and which as `parent`.
  177. * @template {UnistNode} Visited
  178. * Node type.
  179. * @template {UnistParent} Ancestor
  180. * Parent type.
  181. */
  182. /**
  183. * @typedef {(
  184. * BuildVisitorFromMatch<
  185. * Matches<Descendant, Check>,
  186. * Extract<Descendant, UnistParent>
  187. * >
  188. * )} BuildVisitorFromDescendants
  189. * Build a typed `Visitor` function from a list of descendants and a test.
  190. *
  191. * It will infer which values are passed as `node` and which as `parent`.
  192. * @template {UnistNode} Descendant
  193. * Node type.
  194. * @template {Test} Check
  195. * Test type.
  196. */
  197. /**
  198. * @typedef {(
  199. * BuildVisitorFromDescendants<
  200. * InclusiveDescendant<Tree>,
  201. * Check
  202. * >
  203. * )} BuildVisitor
  204. * Build a typed `Visitor` function from a tree and a test.
  205. *
  206. * It will infer which values are passed as `node` and which as `parent`.
  207. * @template {UnistNode} [Tree=UnistNode]
  208. * Node type.
  209. * @template {Test} [Check=Test]
  210. * Test type.
  211. */
  212. import {visitParents} from 'unist-util-visit-parents'
  213. export {CONTINUE, EXIT, SKIP} from 'unist-util-visit-parents'
  214. /**
  215. * Visit nodes.
  216. *
  217. * This algorithm performs *depth-first* *tree traversal* in *preorder*
  218. * (**NLR**) or if `reverse` is given, in *reverse preorder* (**NRL**).
  219. *
  220. * You can choose for which nodes `visitor` is called by passing a `test`.
  221. * For complex tests, you should test yourself in `visitor`, as it will be
  222. * faster and will have improved type information.
  223. *
  224. * Walking the tree is an intensive task.
  225. * Make use of the return values of the visitor when possible.
  226. * Instead of walking a tree multiple times, walk it once, use `unist-util-is`
  227. * to check if a node matches, and then perform different operations.
  228. *
  229. * You can change the tree.
  230. * See `Visitor` for more info.
  231. *
  232. * @overload
  233. * @param {Tree} tree
  234. * @param {Check} check
  235. * @param {BuildVisitor<Tree, Check>} visitor
  236. * @param {boolean | null | undefined} [reverse]
  237. * @returns {undefined}
  238. *
  239. * @overload
  240. * @param {Tree} tree
  241. * @param {BuildVisitor<Tree>} visitor
  242. * @param {boolean | null | undefined} [reverse]
  243. * @returns {undefined}
  244. *
  245. * @param {UnistNode} tree
  246. * Tree to traverse.
  247. * @param {Visitor | Test} testOrVisitor
  248. * `unist-util-is`-compatible test (optional, omit to pass a visitor).
  249. * @param {Visitor | boolean | null | undefined} [visitorOrReverse]
  250. * Handle each node (when test is omitted, pass `reverse`).
  251. * @param {boolean | null | undefined} [maybeReverse=false]
  252. * Traverse in reverse preorder (NRL) instead of the default preorder (NLR).
  253. * @returns {undefined}
  254. * Nothing.
  255. *
  256. * @template {UnistNode} Tree
  257. * Node type.
  258. * @template {Test} Check
  259. * `unist-util-is`-compatible test.
  260. */
  261. export function visit(tree, testOrVisitor, visitorOrReverse, maybeReverse) {
  262. /** @type {boolean | null | undefined} */
  263. let reverse
  264. /** @type {Test} */
  265. let test
  266. /** @type {Visitor} */
  267. let visitor
  268. if (
  269. typeof testOrVisitor === 'function' &&
  270. typeof visitorOrReverse !== 'function'
  271. ) {
  272. test = undefined
  273. visitor = testOrVisitor
  274. reverse = visitorOrReverse
  275. } else {
  276. // @ts-expect-error: assume the overload with test was given.
  277. test = testOrVisitor
  278. // @ts-expect-error: assume the overload with test was given.
  279. visitor = visitorOrReverse
  280. reverse = maybeReverse
  281. }
  282. visitParents(tree, test, overload, reverse)
  283. /**
  284. * @param {UnistNode} node
  285. * @param {Array<UnistParent>} parents
  286. */
  287. function overload(node, parents) {
  288. const parent = parents[parents.length - 1]
  289. const index = parent ? parent.children.indexOf(node) : undefined
  290. return visitor(node, index, parent)
  291. }
  292. }