Skip to content

Commit fb537e4

Browse files
committed
Change to improve types
Now it’s know what the actual parent of a heading can be, and what siblings can be passed and can be returned.
1 parent 7aa2162 commit fb537e4

File tree

2 files changed

+107
-13
lines changed

2 files changed

+107
-13
lines changed

lib/index.js

+101-12
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,115 @@
11
/**
2-
* @typedef {import('mdast').Content} Content
32
* @typedef {import('mdast').Heading} Heading
43
* @typedef {import('mdast').Root} Root
4+
* @typedef {import('unist').Node} UnistNode
55
* @typedef {import('unist').Parent} UnistParent
66
*/
77

8-
// To do: use `Nodes`, `Parents` from `mdast` when released.
98
/**
10-
* @typedef {Content | Root} Node
11-
* @typedef {Extract<Node, UnistParent>} Parent
9+
* @typedef {0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10} Uint
10+
* Number; capped reasonably.
11+
*/
12+
13+
/**
14+
* @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
15+
* Increment a number in the type system.
16+
* @template {Uint} [I=0]
17+
* Index.
18+
*/
19+
20+
/**
21+
* @typedef {(
22+
* Tree extends UnistParent
23+
* ? Depth extends Max
24+
* ? Tree
25+
* : Tree | InclusiveDescendant<Tree['children'][number], Max, Increment<Depth>>
26+
* : Tree
27+
* )} InclusiveDescendant
28+
* Collect all (inclusive) descendants of `Tree`.
29+
*
30+
* > 👉 **Note**: for performance reasons, this seems to be the fastest way to
31+
* > recurse without actually running into an infinite loop, which the
32+
* > previous version did.
33+
* >
34+
* > Practically, a max of `2` is typically enough assuming a `Root` is
35+
* > passed, but it doesn’t improve performance.
36+
* > It gets higher with `List > ListItem > Table > TableRow > TableCell`.
37+
* > Using up to `10` doesn’t hurt or help either.
38+
* @template {UnistNode} Tree
39+
* Tree type.
40+
* @template {Uint} [Max=10]
41+
* Max; searches up to this depth.
42+
* @template {Uint} [Depth=0]
43+
* Current depth.
44+
*/
45+
46+
/**
47+
* @typedef {(
48+
* Node extends {children: Array<infer Child>}
49+
* ? Child
50+
* : never
51+
* )} InternalChild
52+
* Collect nodes that can be parents of `Child`.
53+
* @template {UnistNode} Node
54+
* All node types in a tree.
55+
* @template {UnistNode} Parent
56+
* Node to search for.
57+
*/
58+
59+
/**
60+
* @typedef {(
61+
* Node extends UnistParent
62+
* ? Node extends {children: Array<infer Children>}
63+
* ? Kind extends Children ? Node : never
64+
* : never
65+
* : never
66+
* )} InternalParent
67+
* Collect nodes that can be parents of `Child`.
68+
* @template {UnistNode} Node
69+
* All node types in a tree.
70+
* @template {UnistNode} Kind
71+
* Node to search for.
72+
*/
73+
74+
/**
75+
* @typedef {InternalChild<InclusiveDescendant<Tree>, Kind>} Child
76+
* Collect nodes in `Tree` that can be parents of `Child`.
77+
* @template {UnistNode} Tree
78+
* All node types in a tree.
79+
* @template {UnistNode} Kind
80+
* Node to search for.
81+
*/
82+
83+
/**
84+
* @typedef {InternalParent<InclusiveDescendant<Tree>, Kind>} Parent
85+
* Collect nodes in `Tree` that can be parents of `Child`.
86+
* @template {UnistNode} Tree
87+
* All node types in a tree.
88+
* @template {UnistNode} Kind
89+
* Node to search for.
90+
*/
91+
92+
/**
93+
* @typedef {Child<Root, Parent<Root, Heading>>} Sibling
94+
* Collect nodes in `Tree` that can be parents of `Child`.
95+
* @template {UnistNode} Tree
96+
* All node types in a tree.
97+
* @template {UnistNode} Node
98+
* Node to search for.
1299
*/
13100

14101
/**
15102
* @callback Handler
16103
* Callback called when a section is found.
17104
* @param {Heading} start
18105
* Start of section (a heading node matching `test`).
19-
* @param {Array<Node>} between
106+
* @param {Array<Sibling<Root, Heading>>} between
20107
* Nodes between `start` and `end`.
21-
* @param {Node | undefined} end
108+
* @param {Sibling<Root, Heading> | undefined} end
22109
* End of section, if any.
23110
* @param {Info} scope
24111
* Extra info.
25-
* @returns {Array<Node | null | undefined> | null | undefined | void}
112+
* @returns {Array<Sibling<Root, Heading> | null | undefined> | null | undefined | void}
26113
* Results.
27114
*
28115
* If nothing is returned, nothing will be changed.
@@ -31,7 +118,7 @@
31118
*
32119
* @typedef Info
33120
* Extra info.
34-
* @property {Parent} parent
121+
* @property {Parent<Root, Heading>} parent
35122
* Parent of the section.
36123
* @property {number} start
37124
* Index of `start` in `parent`.
@@ -78,7 +165,7 @@ import {toString} from 'mdast-util-to-string'
78165
* If `ignoreFinalDefinitions: true`, final definitions “in” the section are
79166
* excluded.
80167
*
81-
* @param {Node} tree
168+
* @param {Parent<Root, Heading>} tree
82169
* Tree to change.
83170
* @param {Options | Test} options
84171
* Configuration.
@@ -91,7 +178,7 @@ import {toString} from 'mdast-util-to-string'
91178
export function headingRange(tree, options, handler) {
92179
let test = options
93180
// To do: smarter types to allow siblings of headings.
94-
/** @type {Array<Node>} */
181+
/** @type {Array<Sibling<Root, Heading>>} */
95182
const children = 'children' in tree ? tree.children : []
96183
let ignoreFinalDefinitions = false
97184

@@ -163,7 +250,9 @@ export function headingRange(tree, options, handler) {
163250
const parent = tree
164251
assert('children' in parent, 'parent is a parent')
165252

166-
const nodes = handler(head, children.slice(start + 1, end), children[end], {
253+
const from = children.slice(start + 1, end)
254+
255+
const nodes = handler(head, from, children[end], {
167256
parent,
168257
start,
169258
end: children[end] ? end : null
@@ -172,7 +261,7 @@ export function headingRange(tree, options, handler) {
172261
if (nodes) {
173262
// Ensure no empty nodes are inserted.
174263
// This could be the case if `end` is in `nodes` but no `end` node exists.
175-
/** @type {Array<Node>} */
264+
/** @type {Array<Sibling<Root, Heading>>} */
176265
const result = []
177266
let index = -1
178267

test.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,12 @@ test('headingRange', async function (t) {
4343
const tree = {type: 'inlineCode', value: 'asd'}
4444

4545
assert.doesNotThrow(function () {
46-
headingRange(tree, 'x', function () {})
46+
headingRange(
47+
// @ts-expect-error: types know that `InlineCode` can’t have `Heading` as a child.
48+
tree,
49+
'x',
50+
function () {}
51+
)
4752
})
4853
}
4954
)

0 commit comments

Comments
 (0)