Skip to content

Commit 8a4f677

Browse files
authored
Add inferring of node in MapFunction from tree
Related-to GH-3. Closes GH-4. Reviewed-by: Titus Wormer <[email protected]>
1 parent ff9a5c7 commit 8a4f677

File tree

5 files changed

+44
-33
lines changed

5 files changed

+44
-33
lines changed

.gitignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
.DS_Store
2-
*.d.ts
2+
index.d.ts
3+
test.d.ts
34
*.log
45
coverage/
56
node_modules/

complex-types.d.ts

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import type {Node, Parent} from 'unist'
2+
3+
/**
4+
* Internal utility to collect all descendants of in `Tree`.
5+
* @see https://github.com/syntax-tree/unist-util-visit-parents/blob/18d36ad/complex-types.d.ts#L43
6+
*/
7+
export type InclusiveDescendant<
8+
Tree extends Node = never,
9+
Found = void
10+
> = Tree extends Parent
11+
?
12+
| Tree
13+
| InclusiveDescendant<
14+
Exclude<Tree['children'][number], Found | Tree>,
15+
Found | Tree
16+
>
17+
: Tree
18+
19+
export type MapFunction<Tree extends Node = Node> = (
20+
node: InclusiveDescendant<Tree>,
21+
index: number | null,
22+
parent: InclusiveDescendant<Tree> | null
23+
) => InclusiveDescendant<Tree>

index.js

+7-24
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,29 @@
11
/**
2-
* @typedef {import('unist').Parent} Parent
3-
* @typedef {import('unist').Position} Position
42
* @typedef {import('unist').Node} Node
5-
* @typedef {Record<string, unknown> & {type: string, position?: Position|undefined}} NodeLike
6-
*/
7-
8-
/**
9-
* Function called with a node to produce a new node.
10-
*
11-
* @callback MapFunction
12-
* @param {NodeLike|Node} node Current node being processed
13-
* @param {number} [index] Index of `node`, or `null`
14-
* @param {Parent} [parent] Parent of `node`, or `null`
15-
* @returns {NodeLike|Node} Node to be used in the new tree. Its children are not used: if the original node has children, those are mapped.
163
*/
174

185
/**
196
* Unist utility to create a new tree by mapping all nodes with the given function.
207
*
21-
* @param {NodeLike|Node} tree Tree to map
22-
* @param {MapFunction} iteratee Function that returns a new node
23-
* @returns {NodeLike|Node} New mapped tree.
8+
* @template {Node} Tree
9+
* @param {Tree} tree Tree to map
10+
* @param {import('./complex-types').MapFunction<Tree>} iteratee Function that returns a new node
11+
* @returns {Tree} New mapped tree.
2412
*/
2513
export function map(tree, iteratee) {
14+
// @ts-expect-error Looks like a children.
2615
return preorder(tree, null, null)
2716

28-
/**
29-
* @param {NodeLike|Node} node
30-
* @param {number} [index]
31-
* @param {Parent} [parent]
32-
* @returns {Node}
33-
*/
17+
/** @type {import('./complex-types').MapFunction<Tree>} */
3418
function preorder(node, index, parent) {
3519
var newNode = Object.assign({}, iteratee(node, index, parent))
3620

3721
if ('children' in node) {
3822
// @ts-expect-error Looks like a parent.
3923
newNode.children = node.children.map(function (
40-
/** @type {Node} */ child,
24+
/** @type {import('./complex-types').InclusiveDescendant<Tree>} */ child,
4125
/** @type {number} */ index
4226
) {
43-
// @ts-expect-error Looks like a parent.
4427
return preorder(child, index, node)
4528
})
4629
}

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@
5252
},
5353
"scripts": {
5454
"prepack": "npm run build && npm run format",
55-
"build": "rimraf \"*.d.ts\" && tsc && type-coverage",
55+
"build": "rimraf \"{index,test}.d.ts\" && tsc && type-coverage",
5656
"format": "remark . -qfo && prettier . -w --loglevel warn && xo --fix",
5757
"test-api": "node test.js",
5858
"test-coverage": "c8 --check-coverage --branches 100 --functions 100 --lines 100 --statements 100 --reporter lcov node test.js",

test.js

+11-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
/**
2-
* @typedef {import('unist').Node} Node
2+
* @typedef {{type: 'leaf', value: string}} Leaf
3+
* @typedef {{type: 'node', children: Array<Node | Leaf>}} Node
4+
* @typedef {{type: 'root', children: Array<Node | Leaf>}} Root
5+
* @typedef {Root | Node | Leaf} AnyNode
36
*/
47

58
import test from 'tape'
@@ -15,13 +18,14 @@ test('unist-util-map', function (t) {
1518

1619
t.deepEqual(
1720
map(
18-
{
21+
/** @type {Root} */
22+
({
1923
type: 'root',
2024
children: [
2125
{type: 'node', children: [{type: 'leaf', value: '1'}]},
2226
{type: 'leaf', value: '2'}
2327
]
24-
},
28+
}),
2529
changeLeaf
2630
),
2731
u('root', [u('node', [u('leaf', 'CHANGED')]), u('leaf', 'CHANGED')]),
@@ -45,8 +49,8 @@ test('unist-util-map', function (t) {
4549
t.end()
4650

4751
/**
48-
* @param {Node} node
49-
* @returns {Node}
52+
* @param {AnyNode} node
53+
* @returns {AnyNode}
5054
*/
5155
function changeLeaf(node) {
5256
return node.type === 'leaf'
@@ -55,8 +59,8 @@ test('unist-util-map', function (t) {
5559
}
5660

5761
/**
58-
* @param {Node} node
59-
* @returns {Node?}
62+
* @param {AnyNode} node
63+
* @returns {AnyNode?}
6064
*/
6165
function nullLeaf(node) {
6266
return node.type === 'leaf' ? null : node

0 commit comments

Comments
 (0)