index.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399
  1. /**
  2. * @import {Node as UnistNode, Parent as UnistParent} from 'unist'
  3. */
  4. /**
  5. * @typedef {Exclude<import('unist-util-is').Test, undefined> | undefined} Test
  6. * Test from `unist-util-is`.
  7. *
  8. * Note: we have remove and add `undefined`, because otherwise when generating
  9. * automatic `.d.ts` files, TS tries to flatten paths from a local perspective,
  10. * which doesn’t work when publishing on npm.
  11. */
  12. /**
  13. * @typedef {(
  14. * Fn extends (value: any) => value is infer Thing
  15. * ? Thing
  16. * : Fallback
  17. * )} Predicate
  18. * Get the value of a type guard `Fn`.
  19. * @template Fn
  20. * Value; typically function that is a type guard (such as `(x): x is Y`).
  21. * @template Fallback
  22. * Value to yield if `Fn` is not a type guard.
  23. */
  24. /**
  25. * @typedef {(
  26. * Check extends null | undefined // No test.
  27. * ? Value
  28. * : Value extends {type: Check} // String (type) test.
  29. * ? Value
  30. * : Value extends Check // Partial test.
  31. * ? Value
  32. * : Check extends Function // Function test.
  33. * ? Predicate<Check, Value> extends Value
  34. * ? Predicate<Check, Value>
  35. * : never
  36. * : never // Some other test?
  37. * )} MatchesOne
  38. * Check whether a node matches a primitive check in the type system.
  39. * @template Value
  40. * Value; typically unist `Node`.
  41. * @template Check
  42. * Value; typically `unist-util-is`-compatible test, but not arrays.
  43. */
  44. /**
  45. * @typedef {(
  46. * Check extends ReadonlyArray<infer T>
  47. * ? MatchesOne<Value, T>
  48. * : Check extends Array<infer T>
  49. * ? MatchesOne<Value, T>
  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 {InternalAncestor<InclusiveDescendant<Tree>, Child>} Ancestor
  110. * Collect nodes in `Tree` that can be ancestors of `Child`.
  111. * @template {UnistNode} Tree
  112. * All node types in a tree.
  113. * @template {UnistNode} Child
  114. * Node to search for.
  115. */
  116. /**
  117. * @typedef {(
  118. * Tree extends UnistParent
  119. * ? Depth extends Max
  120. * ? Tree
  121. * : Tree | InclusiveDescendant<Tree['children'][number], Max, Increment<Depth>>
  122. * : Tree
  123. * )} InclusiveDescendant
  124. * Collect all (inclusive) descendants of `Tree`.
  125. *
  126. * > 👉 **Note**: for performance reasons, this seems to be the fastest way to
  127. * > recurse without actually running into an infinite loop, which the
  128. * > previous version did.
  129. * >
  130. * > Practically, a max of `2` is typically enough assuming a `Root` is
  131. * > passed, but it doesn’t improve performance.
  132. * > It gets higher with `List > ListItem > Table > TableRow > TableCell`.
  133. * > Using up to `10` doesn’t hurt or help either.
  134. * @template {UnistNode} Tree
  135. * Tree type.
  136. * @template {Uint} [Max=10]
  137. * Max; searches up to this depth.
  138. * @template {Uint} [Depth=0]
  139. * Current depth.
  140. */
  141. /**
  142. * @typedef {'skip' | boolean} Action
  143. * Union of the action types.
  144. *
  145. * @typedef {number} Index
  146. * Move to the sibling at `index` next (after node itself is completely
  147. * traversed).
  148. *
  149. * Useful if mutating the tree, such as removing the node the visitor is
  150. * currently on, or any of its previous siblings.
  151. * Results less than 0 or greater than or equal to `children.length` stop
  152. * traversing the parent.
  153. *
  154. * @typedef {[(Action | null | undefined | void)?, (Index | null | undefined)?]} ActionTuple
  155. * List with one or two values, the first an action, the second an index.
  156. *
  157. * @typedef {Action | ActionTuple | Index | null | undefined | void} VisitorResult
  158. * Any value that can be returned from a visitor.
  159. */
  160. /**
  161. * @callback Visitor
  162. * Handle a node (matching `test`, if given).
  163. *
  164. * Visitors are free to transform `node`.
  165. * They can also transform the parent of node (the last of `ancestors`).
  166. *
  167. * Replacing `node` itself, if `SKIP` is not returned, still causes its
  168. * descendants to be walked (which is a bug).
  169. *
  170. * When adding or removing previous siblings of `node` (or next siblings, in
  171. * case of reverse), the `Visitor` should return a new `Index` to specify the
  172. * sibling to traverse after `node` is traversed.
  173. * Adding or removing next siblings of `node` (or previous siblings, in case
  174. * of reverse) is handled as expected without needing to return a new `Index`.
  175. *
  176. * Removing the children property of an ancestor still results in them being
  177. * traversed.
  178. * @param {Visited} node
  179. * Found node.
  180. * @param {Array<VisitedParents>} ancestors
  181. * Ancestors of `node`.
  182. * @returns {VisitorResult}
  183. * What to do next.
  184. *
  185. * An `Index` is treated as a tuple of `[CONTINUE, Index]`.
  186. * An `Action` is treated as a tuple of `[Action]`.
  187. *
  188. * Passing a tuple back only makes sense if the `Action` is `SKIP`.
  189. * When the `Action` is `EXIT`, that action can be returned.
  190. * When the `Action` is `CONTINUE`, `Index` can be returned.
  191. * @template {UnistNode} [Visited=UnistNode]
  192. * Visited node type.
  193. * @template {UnistParent} [VisitedParents=UnistParent]
  194. * Ancestor type.
  195. */
  196. /**
  197. * @typedef {Visitor<Matches<InclusiveDescendant<Tree>, Check>, Ancestor<Tree, Matches<InclusiveDescendant<Tree>, Check>>>} BuildVisitor
  198. * Build a typed `Visitor` function from a tree and a test.
  199. *
  200. * It will infer which values are passed as `node` and which as `parents`.
  201. * @template {UnistNode} [Tree=UnistNode]
  202. * Tree type.
  203. * @template {Test} [Check=Test]
  204. * Test type.
  205. */
  206. import {convert} from 'unist-util-is'
  207. import {color} from 'unist-util-visit-parents/do-not-use-color'
  208. /** @type {Readonly<ActionTuple>} */
  209. const empty = []
  210. /**
  211. * Continue traversing as normal.
  212. */
  213. export const CONTINUE = true
  214. /**
  215. * Stop traversing immediately.
  216. */
  217. export const EXIT = false
  218. /**
  219. * Do not traverse this node’s children.
  220. */
  221. export const SKIP = 'skip'
  222. /**
  223. * Visit nodes, with ancestral information.
  224. *
  225. * This algorithm performs *depth-first* *tree traversal* in *preorder*
  226. * (**NLR**) or if `reverse` is given, in *reverse preorder* (**NRL**).
  227. *
  228. * You can choose for which nodes `visitor` is called by passing a `test`.
  229. * For complex tests, you should test yourself in `visitor`, as it will be
  230. * faster and will have improved type information.
  231. *
  232. * Walking the tree is an intensive task.
  233. * Make use of the return values of the visitor when possible.
  234. * Instead of walking a tree multiple times, walk it once, use `unist-util-is`
  235. * to check if a node matches, and then perform different operations.
  236. *
  237. * You can change the tree.
  238. * See `Visitor` for more info.
  239. *
  240. * @overload
  241. * @param {Tree} tree
  242. * @param {Check} check
  243. * @param {BuildVisitor<Tree, Check>} visitor
  244. * @param {boolean | null | undefined} [reverse]
  245. * @returns {undefined}
  246. *
  247. * @overload
  248. * @param {Tree} tree
  249. * @param {BuildVisitor<Tree>} visitor
  250. * @param {boolean | null | undefined} [reverse]
  251. * @returns {undefined}
  252. *
  253. * @param {UnistNode} tree
  254. * Tree to traverse.
  255. * @param {Visitor | Test} test
  256. * `unist-util-is`-compatible test
  257. * @param {Visitor | boolean | null | undefined} [visitor]
  258. * Handle each node.
  259. * @param {boolean | null | undefined} [reverse]
  260. * Traverse in reverse preorder (NRL) instead of the default preorder (NLR).
  261. * @returns {undefined}
  262. * Nothing.
  263. *
  264. * @template {UnistNode} Tree
  265. * Node type.
  266. * @template {Test} Check
  267. * `unist-util-is`-compatible test.
  268. */
  269. export function visitParents(tree, test, visitor, reverse) {
  270. /** @type {Test} */
  271. let check
  272. if (typeof test === 'function' && typeof visitor !== 'function') {
  273. reverse = visitor
  274. // @ts-expect-error no visitor given, so `visitor` is test.
  275. visitor = test
  276. } else {
  277. // @ts-expect-error visitor given, so `test` isn’t a visitor.
  278. check = test
  279. }
  280. const is = convert(check)
  281. const step = reverse ? -1 : 1
  282. factory(tree, undefined, [])()
  283. /**
  284. * @param {UnistNode} node
  285. * @param {number | undefined} index
  286. * @param {Array<UnistParent>} parents
  287. */
  288. function factory(node, index, parents) {
  289. const value = /** @type {Record<string, unknown>} */ (
  290. node && typeof node === 'object' ? node : {}
  291. )
  292. if (typeof value.type === 'string') {
  293. const name =
  294. // `hast`
  295. typeof value.tagName === 'string'
  296. ? value.tagName
  297. : // `xast`
  298. typeof value.name === 'string'
  299. ? value.name
  300. : undefined
  301. Object.defineProperty(visit, 'name', {
  302. value:
  303. 'node (' + color(node.type + (name ? '<' + name + '>' : '')) + ')'
  304. })
  305. }
  306. return visit
  307. function visit() {
  308. /** @type {Readonly<ActionTuple>} */
  309. let result = empty
  310. /** @type {Readonly<ActionTuple>} */
  311. let subresult
  312. /** @type {number} */
  313. let offset
  314. /** @type {Array<UnistParent>} */
  315. let grandparents
  316. if (!test || is(node, index, parents[parents.length - 1] || undefined)) {
  317. // @ts-expect-error: `visitor` is now a visitor.
  318. result = toResult(visitor(node, parents))
  319. if (result[0] === EXIT) {
  320. return result
  321. }
  322. }
  323. if ('children' in node && node.children) {
  324. const nodeAsParent = /** @type {UnistParent} */ (node)
  325. if (nodeAsParent.children && result[0] !== SKIP) {
  326. offset = (reverse ? nodeAsParent.children.length : -1) + step
  327. grandparents = parents.concat(nodeAsParent)
  328. while (offset > -1 && offset < nodeAsParent.children.length) {
  329. const child = nodeAsParent.children[offset]
  330. subresult = factory(child, offset, grandparents)()
  331. if (subresult[0] === EXIT) {
  332. return subresult
  333. }
  334. offset =
  335. typeof subresult[1] === 'number' ? subresult[1] : offset + step
  336. }
  337. }
  338. }
  339. return result
  340. }
  341. }
  342. }
  343. /**
  344. * Turn a return value into a clean result.
  345. *
  346. * @param {VisitorResult} value
  347. * Valid return values from visitors.
  348. * @returns {Readonly<ActionTuple>}
  349. * Clean result.
  350. */
  351. function toResult(value) {
  352. if (Array.isArray(value)) {
  353. return value
  354. }
  355. if (typeof value === 'number') {
  356. return [CONTINUE, value]
  357. }
  358. return value === null || value === undefined ? empty : [value]
  359. }