index.js 9.4 KB

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