Skip to content

Commit

Permalink
Refactor to move implementation to lib/
Browse files Browse the repository at this point in the history
  • Loading branch information
wooorm committed Jan 24, 2023
1 parent 69cbec6 commit 9f4813b
Show file tree
Hide file tree
Showing 7 changed files with 182 additions and 117 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
.DS_Store
index.d.ts
lib/index.d.ts
test.d.ts
*.log
coverage/
Expand Down
56 changes: 2 additions & 54 deletions complex-types.d.ts
Original file line number Diff line number Diff line change
@@ -1,54 +1,2 @@
import type {Node, Parent} from 'unist'
import type {Test} from 'unist-util-is'
import type {
VisitorResult,
Matches,
InclusiveDescendant
} from 'unist-util-visit-parents/complex-types.js'

/**
* Called when a node (matching test, if given) is found.
* Visitors are free to transform node.
* They can also transform the parent of node (the last of ancestors).
* Replacing node itself, if `SKIP` is not returned, still causes its descendants to be visited.
* If adding or removing previous siblings (or next siblings, in case of reverse) of node,
* visitor should return a new index (number) 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 an ancestor still results in them being traversed.
*/
export type Visitor<
Visited extends Node = Node,
Ancestor extends Parent = Parent
> = (
node: Visited,
index: Visited extends Node ? number | null : never,
parent: Ancestor extends Node ? Ancestor | null : Ancestor
) => VisitorResult

type ParentsOf<
Ancestor extends Node,
Child extends Node
> = Ancestor extends Parent
? Child extends Ancestor['children'][number]
? Ancestor
: never
: never

type BuildVisitorFromMatch<
Visited extends Node,
Ancestor extends Parent
> = Visitor<Visited, ParentsOf<Ancestor, Visited>>

type BuildVisitorFromDescendants<
Descendant extends Node,
Check extends Test
> = BuildVisitorFromMatch<
Matches<Descendant, Check>,
Extract<Descendant, Parent>
>

export type BuildVisitor<
Tree extends Node = Node,
Check extends Test = string
> = BuildVisitorFromDescendants<InclusiveDescendant<Tree>, Check>
// To do: next major: remove this file.
export type {Visitor, BuildVisitor} from './index.js'
10 changes: 10 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export type {Test} from 'unist-util-is'
export type {
Action,
ActionTuple,
Index,
VisitorResult
} from 'unist-util-visit-parents'
export {CONTINUE, EXIT, SKIP} from 'unist-util-visit-parents'
export type {Visitor, BuildVisitor} from './lib/index.js'
export {visit} from './lib/index.js'
62 changes: 2 additions & 60 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,61 +1,3 @@
/**
* @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
* @typedef {import('./complex-types.js').Visitor} Visitor
*/

import {visitParents} from 'unist-util-visit-parents'

/**
* Visit children of tree which pass test.
*
* @param tree
* Tree to walk
* @param [test]
* `unist-util-is`-compatible test
* @param visitor
* Function called for nodes that pass `test`.
* @param reverse
* Traverse in reverse preorder (NRL) instead of preorder (NLR) (default).
*/
export const visit =
/**
* @type {(
* (<Tree extends Node, Check extends Test>(tree: Tree, test: Check, visitor: import('./complex-types.js').BuildVisitor<Tree, Check>, reverse?: boolean) => void) &
* (<Tree extends Node>(tree: Tree, visitor: import('./complex-types.js').BuildVisitor<Tree>, reverse?: boolean) => void)
* )}
*/
(
/**
* @param {Node} tree
* @param {Test} test
* @param {import('./complex-types.js').Visitor} visitor
* @param {boolean} [reverse]
*/
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
)
}
}
)

// Note: types exported from `index.d.ts`
export {CONTINUE, EXIT, SKIP} from 'unist-util-visit-parents'
export {visit} from './lib/index.js'
164 changes: 164 additions & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
/**
* @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 children of tree which pass test.
*
* @param tree
* Tree to walk
* @param [test]
* `unist-util-is`-compatible test
* @param visitor
* Function called for nodes that pass `test`.
* @param reverse
* Traverse in reverse preorder (NRL) instead of preorder (NLR) (default).
*/
export const visit =
/**
* @type {(
* (<Tree extends Node, Check extends Test>(tree: Tree, test: Check, visitor: BuildVisitor<Tree, Check>, reverse?: boolean) => void) &
* (<Tree extends Node>(tree: Tree, visitor: BuildVisitor<Tree>, reverse?: boolean) => void)
* )}
*/
(
/**
* @param {Node} tree
* @param {Test} test
* @param {Visitor} visitor
* @param {boolean} [reverse]
*/
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'
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
"main": "index.js",
"types": "index.d.ts",
"files": [
"lib/",
"complex-types.d.ts",
"index.d.ts",
"index.js"
Expand Down Expand Up @@ -92,7 +93,7 @@
},
"remarkConfig": {
"plugins": [
"preset-wooorm"
"remark-preset-wooorm"
]
},
"typeCoverage": {
Expand Down
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"include": ["**/*.js", "complex-types.d.ts"],
"include": ["**/*.js", "complex-types.d.ts", "index.d.ts"],
"exclude": ["coverage/", "node_modules/"],
"compilerOptions": {
"checkJs": true,
Expand Down

0 comments on commit 9f4813b

Please sign in to comment.