183 lines
		
	
	
	
		
			5.3 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
		
		
			
		
	
	
			183 lines
		
	
	
	
		
			5.3 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
|   | /** | ||
|  |  * @typedef {import('unist').Node} Node | ||
|  |  * @typedef {import('unist').Parent} Parent | ||
|  |  * @typedef {import('unist-util-is').Test} Test | ||
|  |  * @typedef {import('unist-util-visit-parents').VisitorResult} VisitorResult | ||
|  |  */ | ||
|  | 
 | ||
|  | /** | ||
|  |  * Check if `Child` can be a child of `Ancestor`. | ||
|  |  * | ||
|  |  * Returns the ancestor when `Child` can be a child of `Ancestor`, or returns | ||
|  |  * `never`. | ||
|  |  * | ||
|  |  * @template {Node} Ancestor | ||
|  |  *   Node type. | ||
|  |  * @template {Node} Child | ||
|  |  *   Node type. | ||
|  |  * @typedef {( | ||
|  |  *   Ancestor extends Parent | ||
|  |  *     ? Child extends Ancestor['children'][number] | ||
|  |  *       ? Ancestor | ||
|  |  *       : never | ||
|  |  *     : never | ||
|  |  * )} ParentsOf | ||
|  |  */ | ||
|  | 
 | ||
|  | /** | ||
|  |  * @template {Node} [Visited=Node] | ||
|  |  *   Visited node type. | ||
|  |  * @template {Parent} [Ancestor=Parent] | ||
|  |  *   Ancestor type. | ||
|  |  * @callback Visitor | ||
|  |  *   Handle a node (matching `test`, if given). | ||
|  |  * | ||
|  |  *   Visitors are free to transform `node`. | ||
|  |  *   They can also transform `parent`. | ||
|  |  * | ||
|  |  *   Replacing `node` itself, if `SKIP` is not returned, still causes its | ||
|  |  *   descendants to be walked (which is a bug). | ||
|  |  * | ||
|  |  *   When adding or removing previous siblings of `node` (or next siblings, in | ||
|  |  *   case of reverse), the `Visitor` should return a new `Index` to specify the | ||
|  |  *   sibling to traverse after `node` is traversed. | ||
|  |  *   Adding or removing next siblings of `node` (or previous siblings, in case | ||
|  |  *   of reverse) is handled as expected without needing to return a new `Index`. | ||
|  |  * | ||
|  |  *   Removing the children property of `parent` still results in them being | ||
|  |  *   traversed. | ||
|  |  * @param {Visited} node | ||
|  |  *   Found node. | ||
|  |  * @param {Visited extends Node ? number | null : never} index | ||
|  |  *   Index of `node` in `parent`. | ||
|  |  * @param {Ancestor extends Node ? Ancestor | null : never} parent | ||
|  |  *   Parent of `node`. | ||
|  |  * @returns {VisitorResult} | ||
|  |  *   What to do next. | ||
|  |  * | ||
|  |  *   An `Index` is treated as a tuple of `[CONTINUE, Index]`. | ||
|  |  *   An `Action` is treated as a tuple of `[Action]`. | ||
|  |  * | ||
|  |  *   Passing a tuple back only makes sense if the `Action` is `SKIP`. | ||
|  |  *   When the `Action` is `EXIT`, that action can be returned. | ||
|  |  *   When the `Action` is `CONTINUE`, `Index` can be returned. | ||
|  |  */ | ||
|  | 
 | ||
|  | /** | ||
|  |  * Build a typed `Visitor` function from a node and all possible parents. | ||
|  |  * | ||
|  |  * It will infer which values are passed as `node` and which as `parent`. | ||
|  |  * | ||
|  |  * @template {Node} Visited | ||
|  |  *   Node type. | ||
|  |  * @template {Parent} Ancestor | ||
|  |  *   Parent type. | ||
|  |  * @typedef {Visitor<Visited, ParentsOf<Ancestor, Visited>>} BuildVisitorFromMatch | ||
|  |  */ | ||
|  | 
 | ||
|  | /** | ||
|  |  * Build a typed `Visitor` function from a list of descendants and a test. | ||
|  |  * | ||
|  |  * It will infer which values are passed as `node` and which as `parent`. | ||
|  |  * | ||
|  |  * @template {Node} Descendant | ||
|  |  *   Node type. | ||
|  |  * @template {Test} Check | ||
|  |  *   Test type. | ||
|  |  * @typedef {( | ||
|  |  *   BuildVisitorFromMatch< | ||
|  |  *     import('unist-util-visit-parents/complex-types.js').Matches<Descendant, Check>, | ||
|  |  *     Extract<Descendant, Parent> | ||
|  |  *   > | ||
|  |  * )} BuildVisitorFromDescendants | ||
|  |  */ | ||
|  | 
 | ||
|  | /** | ||
|  |  * Build a typed `Visitor` function from a tree and a test. | ||
|  |  * | ||
|  |  * It will infer which values are passed as `node` and which as `parent`. | ||
|  |  * | ||
|  |  * @template {Node} [Tree=Node] | ||
|  |  *   Node type. | ||
|  |  * @template {Test} [Check=string] | ||
|  |  *   Test type. | ||
|  |  * @typedef {( | ||
|  |  *   BuildVisitorFromDescendants< | ||
|  |  *     import('unist-util-visit-parents/complex-types.js').InclusiveDescendant<Tree>, | ||
|  |  *     Check | ||
|  |  *   > | ||
|  |  * )} BuildVisitor | ||
|  |  */ | ||
|  | 
 | ||
|  | import {visitParents} from 'unist-util-visit-parents' | ||
|  | 
 | ||
|  | /** | ||
|  |  * Visit nodes. | ||
|  |  * | ||
|  |  * This algorithm performs *depth-first* *tree traversal* in *preorder* | ||
|  |  * (**NLR**) or if `reverse` is given, in *reverse preorder* (**NRL**). | ||
|  |  * | ||
|  |  * You can choose for which nodes `visitor` is called by passing a `test`. | ||
|  |  * For complex tests, you should test yourself in `visitor`, as it will be | ||
|  |  * faster and will have improved type information. | ||
|  |  * | ||
|  |  * Walking the tree is an intensive task. | ||
|  |  * Make use of the return values of the visitor when possible. | ||
|  |  * Instead of walking a tree multiple times, walk it once, use `unist-util-is` | ||
|  |  * to check if a node matches, and then perform different operations. | ||
|  |  * | ||
|  |  * You can change the tree. | ||
|  |  * See `Visitor` for more info. | ||
|  |  * | ||
|  |  * @param tree | ||
|  |  *   Tree to traverse. | ||
|  |  * @param test | ||
|  |  *   `unist-util-is`-compatible test | ||
|  |  * @param visitor | ||
|  |  *   Handle each node. | ||
|  |  * @param reverse | ||
|  |  *   Traverse in reverse preorder (NRL) instead of the default preorder (NLR). | ||
|  |  * @returns | ||
|  |  *   Nothing. | ||
|  |  */ | ||
|  | export const visit = | ||
|  |   /** | ||
|  |    * @type {( | ||
|  |    *   (<Tree extends Node, Check extends Test>(tree: Tree, test: Check, visitor: BuildVisitor<Tree, Check>, reverse?: boolean | null | undefined) => void) & | ||
|  |    *   (<Tree extends Node>(tree: Tree, visitor: BuildVisitor<Tree>, reverse?: boolean | null | undefined) => void) | ||
|  |    * )} | ||
|  |    */ | ||
|  |   ( | ||
|  |     /** | ||
|  |      * @param {Node} tree | ||
|  |      * @param {Test} test | ||
|  |      * @param {Visitor} visitor | ||
|  |      * @param {boolean | null | undefined} [reverse] | ||
|  |      * @returns {void} | ||
|  |      */ | ||
|  |     function (tree, test, visitor, reverse) { | ||
|  |       if (typeof test === 'function' && typeof visitor !== 'function') { | ||
|  |         reverse = visitor | ||
|  |         visitor = test | ||
|  |         test = null | ||
|  |       } | ||
|  | 
 | ||
|  |       visitParents(tree, test, overload, reverse) | ||
|  | 
 | ||
|  |       /** | ||
|  |        * @param {Node} node | ||
|  |        * @param {Array<Parent>} parents | ||
|  |        */ | ||
|  |       function overload(node, parents) { | ||
|  |         const parent = parents[parents.length - 1] | ||
|  |         return visitor( | ||
|  |           node, | ||
|  |           parent ? parent.children.indexOf(node) : null, | ||
|  |           parent | ||
|  |         ) | ||
|  |       } | ||
|  |     } | ||
|  |   ) | ||
|  | 
 | ||
|  | export {CONTINUE, EXIT, SKIP} from 'unist-util-visit-parents' |