1
1
/**
2
- * @typedef {import('unist').Parent } Parent
3
- * @typedef {import('mdast').Root|import('mdast'). Content } Node
2
+ * @typedef {import('unist').Parent } UnistParent
3
+ * @typedef {import('mdast').Content } Content
4
4
* @typedef {import('mdast').Heading } Heading
5
+ * @typedef {import('mdast').Root } Root
6
+ */
7
+
8
+ /**
9
+ * @typedef {Content | Root } Node
10
+ * @typedef {Extract<Node, UnistParent> } Parent
5
11
*
6
- * @typedef {(value: string, node: Heading) => boolean } TestFunction
7
- * Function called for each heading with its content and `node` itself check
8
- * if it’s the one to look for.
12
+ * @callback TestFunction
13
+ * Function called with headings to check if it’s the one to look for.
14
+ * @param {string } value
15
+ * Plain-text heading.
16
+ * @param {Heading } node
17
+ * Heading node.
18
+ * @returns {boolean }
19
+ * Whether this is the heading that is searched for.
9
20
*
10
- * @typedef {string|RegExp|TestFunction } Test
21
+ * @typedef {string | RegExp | TestFunction } Test
22
+ * A test.
11
23
*
12
24
* @typedef Options
13
25
* Configuration (optional).
14
26
* @property {Test } test
15
27
* Heading to look for.
28
+ *
16
29
* When `string`, wrapped in `new RegExp('^(' + value + ')$', 'i')`;
17
- * when `RegExp`, wrapped in `function (value) { expression.test(value)} `
18
- * @property {boolean } [ignoreFinalDefinitions=false]
30
+ * when `RegExp`, wrapped in `(value) => expression.test(value)`
31
+ * @property {boolean | null | undefined } [ignoreFinalDefinitions=false]
19
32
* Ignore final definitions otherwise in the section.
20
33
*
21
34
* @typedef ZoneInfo
22
35
* Extra info.
23
- * @property {Parent| null } parent
36
+ * @property {Parent | null } parent
24
37
* Parent of the range.
25
38
* @property {number } start
26
39
* index of `start` in `parent`
27
- * @property {number } end
40
+ * @property {number | null } end
28
41
* index of `end` in `parent`
29
42
*
30
43
* @callback Handler
31
44
* Callback called when a range is found.
32
- * @param {Heading|undefined } start
33
- * Start of range.
45
+ * @param {Heading } start
46
+ * Start of range (a heading node matching `test`) .
34
47
* @param {Array<Node> } between
35
48
* Nodes between `start` and `end`.
36
- * @param {Node| undefined } end
49
+ * @param {Node | undefined } end
37
50
* End of range, if any.
38
51
* @param {ZoneInfo } scope
39
52
* Extra info.
53
+ * @returns {Array<Node | null | undefined> | null | undefined | void }
54
+ * Results.
55
+ *
56
+ * If nothing is returned, nothing will be changed.
57
+ * If an array of nodes (can include `null` and `undefined`) is returned, the
58
+ * original section will be replaced by those nodes.
40
59
*/
41
60
42
61
import { toString } from 'mdast-util-to-string'
@@ -47,24 +66,29 @@ import {toString} from 'mdast-util-to-string'
47
66
*
48
67
* A “section” is a heading that passes `test`, until the next heading of the
49
68
* same or lower depth, or the end of the document.
69
+ *
50
70
* If `ignoreFinalDefinitions: true`, final definitions “in” the section are
51
71
* excluded.
52
72
*
53
73
* @param {Node } tree
54
- * @param {Test|Options } options
74
+ * Tree to change.
75
+ * @param {Test | Options } options
76
+ * Configuration.
55
77
* @param {Handler } handler
78
+ * Handle a range.
79
+ * @returns {void }
80
+ * Nothing.
56
81
*/
57
82
// eslint-disable-next-line complexity
58
83
export function headingRange ( tree , options , handler ) {
59
84
let test = options
60
85
/** @type {Array<Node> } */
61
86
const children = 'children' in tree ? tree . children : [ ]
62
- /** @type {boolean|undefined } */
63
- let ignoreFinalDefinitions
87
+ let ignoreFinalDefinitions = false
64
88
65
89
// Object, not regex.
66
90
if ( test && typeof test === 'object' && ! ( 'exec' in test ) ) {
67
- ignoreFinalDefinitions = test . ignoreFinalDefinitions
91
+ ignoreFinalDefinitions = test . ignoreFinalDefinitions === true
68
92
test = test . test
69
93
}
70
94
@@ -87,11 +111,11 @@ export function headingRange(tree, options, handler) {
87
111
}
88
112
89
113
let index = - 1
90
- /** @type {number| undefined } */
114
+ /** @type {number | undefined } */
91
115
let start
92
- /** @type {number| undefined } */
116
+ /** @type {number | undefined } */
93
117
let end
94
- /** @type {number|undefined } */
118
+ /** @type {number| undefined } */
95
119
let depth
96
120
97
121
// Find the range.
@@ -124,14 +148,18 @@ export function headingRange(tree, options, handler) {
124
148
}
125
149
}
126
150
127
- /** @type {Array<Node> } */
128
- const nodes = handler (
129
- // @ts -expect-error `start` points to a heading.
130
- children [ start ] ,
131
- children . slice ( start + 1 , end ) ,
132
- children [ end ] ,
133
- { parent : tree , start, end : children [ end ] ? end : null }
134
- )
151
+ /** @type {Heading } */
152
+ // @ts -expect-error `start` points to a heading.
153
+ const head = children [ start ]
154
+ /** @type {Parent } */
155
+ // @ts -expect-error always a parent, or we don’t come here.
156
+ const parent = tree
157
+
158
+ const nodes = handler ( head , children . slice ( start + 1 , end ) , children [ end ] , {
159
+ parent,
160
+ start,
161
+ end : children [ end ] ? end : null
162
+ } )
135
163
136
164
if ( nodes ) {
137
165
// Ensure no empty nodes are inserted.
@@ -141,7 +169,8 @@ export function headingRange(tree, options, handler) {
141
169
let index = - 1
142
170
143
171
while ( ++ index < nodes . length ) {
144
- if ( nodes [ index ] ) result . push ( nodes [ index ] )
172
+ const node = nodes [ index ]
173
+ if ( node ) result . push ( node )
145
174
}
146
175
147
176
children . splice ( start , end - start + 1 , ...result )
@@ -151,17 +180,16 @@ export function headingRange(tree, options, handler) {
151
180
152
181
/**
153
182
* Wrap an expression into an assertion function.
183
+ *
154
184
* @param {RegExp } expression
155
- * @returns {(value: string) => boolean }
185
+ * Test expression.
186
+ * @returns {TestFunction }
187
+ * Test function.
156
188
*/
157
189
function wrapExpression ( expression ) {
158
190
return assertion
159
191
160
- /**
161
- * Assert `value` matches the bound `expression`.
162
- * @param {string } value
163
- * @returns {boolean }
164
- */
192
+ /** @type {TestFunction } */
165
193
function assertion ( value ) {
166
194
return expression . test ( value )
167
195
}
0 commit comments