1
+ /**
2
+ * @typedef {import('hast-util-is-element').TestFunctionAnything } TestFunctionAnything
3
+ * @typedef {import('hast').Parent['children'][number] } HastChild
4
+ * @typedef {import('hast').Text } HastText
5
+ * @typedef {import('hast').Comment } HastComment
6
+ * @typedef {import('hast').Root } HastRoot
7
+ * @typedef {import('hast').Element } HastElement
8
+ * @typedef {import('hast').Properties } HastProperties
9
+ * @typedef {HastChild|HastRoot } HastNode
10
+ * @typedef {HastRoot|HastElement } HastParent
11
+ *
12
+ * @typedef {'normal'|'pre'|'nowrap'|'pre-wrap' } Whitespace
13
+ * @typedef {boolean } BreakValue
14
+ * @typedef {1|2 } BreakNumber
15
+ * @typedef {'\n' } BreakForce
16
+ * @typedef {BreakValue|BreakNumber } BreakBefore
17
+ * @typedef {BreakValue|BreakNumber|BreakForce } BreakAfter
18
+ *
19
+ * @typedef CollectionOptions
20
+ * @property {Whitespace } whitespace
21
+ * @property {BreakBefore } breakBefore
22
+ * @property {BreakAfter } breakAfter
23
+ */
24
+
1
25
import repeat from 'repeat-string'
2
26
import { convertElement } from 'hast-util-is-element'
3
27
import { findAfter } from 'unist-util-find-after'
@@ -73,18 +97,33 @@ var blockOrCaption = convertElement([
73
97
'xmp' // Flow content (legacy)
74
98
] )
75
99
76
- // Implementation of the `innerText` getter:
77
- // <https://html.spec.whatwg.org/#the-innertext-idl-attribute>
78
- // Note that we act as if `node` is being rendered, and as if we’re a
79
- // CSS-supporting user agent.
100
+ /**
101
+ * Implementation of the `innerText` getter:
102
+ * <https://html.spec.whatwg.org/#the-innertext-idl-attribute>
103
+ * Note that we act as if `node` is being rendered, and as if we’re a
104
+ * CSS-supporting user agent.
105
+ *
106
+ * @param {HastNode } node
107
+ * @returns {string }
108
+ */
80
109
export function toText ( node ) {
110
+ /** @type {Array.<HastChild> } */
111
+ // @ts -ignore looks like a parent.
81
112
var children = node . children || [ ]
82
113
var block = blockOrCaption ( node )
83
- var whitespace = inferWhitespace ( node , { } )
114
+ var whitespace = inferWhitespace ( node , {
115
+ whitespace : 'normal' ,
116
+ breakBefore : false ,
117
+ breakAfter : false
118
+ } )
84
119
var index = - 1
120
+ /** @type {Array.<string|BreakNumber> } */
85
121
var results
122
+ /** @type {Array.<string> } */
86
123
var result
124
+ /** @type {string|BreakNumber } */
87
125
var value
126
+ /** @type {number } */
88
127
var count
89
128
90
129
// Treat `text` and `comment` as having normal white-space.
@@ -96,11 +135,7 @@ export function toText(node) {
96
135
// Nodes without children are treated as a void element, so `doctype` is thus
97
136
// ignored.
98
137
if ( node . type === 'text' || node . type === 'comment' ) {
99
- return collectText ( node , {
100
- whitespace,
101
- breakBefore : true ,
102
- breakAfter : true
103
- } )
138
+ return collectText ( node , { whitespace, breakBefore : true , breakAfter : true } )
104
139
}
105
140
106
141
// 1. If this element is not being rendered, or if the user agent is a
@@ -124,7 +159,8 @@ export function toText(node) {
124
159
// positive integer (a required line break count).
125
160
// 3.2. For each item item in current, append item to results.
126
161
results = results . concat (
127
- innerTextCollection ( children [ index ] , index , node , {
162
+ // @ts -ignore Looks like a parent.
163
+ innerTextCollection ( children [ index ] , node , {
128
164
whitespace,
129
165
breakBefore : index ? null : block ,
130
166
breakAfter :
@@ -159,31 +195,47 @@ export function toText(node) {
159
195
return result . join ( '' )
160
196
}
161
197
162
- // <https://html.spec.whatwg.org/#inner-text-collection-steps>
163
- function innerTextCollection ( node , index , parent , options ) {
198
+ /**
199
+ * <https://html.spec.whatwg.org/#inner-text-collection-steps>
200
+ *
201
+ * @param {HastNode } node
202
+ * @param {HastParent } parent
203
+ * @param {CollectionOptions } options
204
+ * @returns {Array.<string|BreakNumber> }
205
+ */
206
+ function innerTextCollection ( node , parent , options ) {
164
207
if ( node . type === 'element' ) {
165
- return collectElement ( node , index , parent , options )
208
+ return collectElement ( node , parent , options )
166
209
}
167
210
168
211
if ( node . type === 'text' ) {
169
212
return [
170
213
options . whitespace === 'normal'
171
214
? collectText ( node , options )
172
- : collectPreText ( node , options )
215
+ : collectPreText ( node )
173
216
]
174
217
}
175
218
176
219
return [ ]
177
220
}
178
221
179
- // Collect an element.
180
- function collectElement ( node , _ , parent , options ) {
222
+ /**
223
+ * Collect an element.
224
+ *
225
+ * @param {HastElement } node
226
+ * @param {HastParent } parent
227
+ * @param {CollectionOptions } options
228
+ */
229
+ function collectElement ( node , parent , options ) {
181
230
// First we infer the `white-space` property.
182
231
var whitespace = inferWhitespace ( node , options )
183
232
var children = node . children || [ ]
184
233
var index = - 1
234
+ /** @type {Array.<string|BreakNumber> } */
185
235
var items = [ ]
236
+ /** @type {BreakNumber } */
186
237
var prefix
238
+ /** @type {BreakNumber|BreakForce } */
187
239
var suffix
188
240
189
241
// We’re ignoring point 3, and exiting without any content here, because we
@@ -244,9 +296,9 @@ function collectElement(node, _, parent, options) {
244
296
// results to a single list.
245
297
while ( ++ index < children . length ) {
246
298
items = items . concat (
247
- innerTextCollection ( children [ index ] , index , node , {
299
+ innerTextCollection ( children [ index ] , node , {
248
300
whitespace,
249
- breakBefore : index ? null : prefix ,
301
+ breakBefore : index ? undefined : prefix ,
250
302
breakAfter :
251
303
index < children . length - 1 ? br ( children [ index + 1 ] ) : suffix
252
304
} )
@@ -270,29 +322,40 @@ function collectElement(node, _, parent, options) {
270
322
return items
271
323
}
272
324
273
- // 4. If node is a Text node, then for each CSS text box produced by node,
274
- // in content order, compute the text of the box after application of the
275
- // CSS `white-space` processing rules and `text-transform` rules, set
276
- // items to the list of the resulting strings, and return items.
277
- // The CSS `white-space` processing rules are slightly modified:
278
- // collapsible spaces at the end of lines are always collapsed, but they
279
- // are only removed if the line is the last line of the block, or it ends
280
- // with a br element.
281
- // Soft hyphens should be preserved.
282
- //
283
- // Note: See `collectText` and `collectPreText`.
284
- // Note: we don’t deal with `text-transform`, no element has that by
285
- // default.
286
- //
287
- // See: <https://drafts.csswg.org/css-text/#white-space-phase-1>
325
+ /**
326
+ * 4. If node is a Text node, then for each CSS text box produced by node,
327
+ * in content order, compute the text of the box after application of the
328
+ * CSS `white-space` processing rules and `text-transform` rules, set
329
+ * items to the list of the resulting strings, and return items.
330
+ * The CSS `white-space` processing rules are slightly modified:
331
+ * collapsible spaces at the end of lines are always collapsed, but they
332
+ * are only removed if the line is the last line of the block, or it ends
333
+ * with a br element.
334
+ * Soft hyphens should be preserved.
335
+ *
336
+ * Note: See `collectText` and `collectPreText`.
337
+ * Note: we don’t deal with `text-transform`, no element has that by
338
+ * default.
339
+ *
340
+ * See: <https://drafts.csswg.org/css-text/#white-space-phase-1>
341
+ *
342
+ * @param {HastText|HastComment } node
343
+ * @param {CollectionOptions } options
344
+ * @returns {string }
345
+ */
288
346
function collectText ( node , options ) {
289
347
var value = String ( node . value )
348
+ /** @type {Array.<string> } */
290
349
var lines = [ ]
350
+ /** @type {Array.<string> } */
291
351
var result = [ ]
292
352
var start = 0
293
353
var index = - 1
354
+ /** @type {RegExpMatchArray } */
294
355
var match
356
+ /** @type {number } */
295
357
var end
358
+ /** @type {string } */
296
359
var join
297
360
298
361
while ( start < value . length ) {
@@ -303,7 +366,7 @@ function collectText(node, options) {
303
366
lines . push (
304
367
// Any sequence of collapsible spaces and tabs immediately preceding or
305
368
// following a segment break is removed.
306
- trimAndcollapseSpacesAndTabs (
369
+ trimAndCollapseSpacesAndTabs (
307
370
// [...] ignoring bidi formatting characters (characters with the
308
371
// Bidi_Control property [UAX9]: ALM, LTR, RTL, LRE-RLO, LRI-PDI) as if
309
372
// they were not there.
@@ -362,20 +425,34 @@ function collectText(node, options) {
362
425
return result . join ( '' )
363
426
}
364
427
428
+ /**
429
+ * @param {HastText|HastComment } node
430
+ * @returns {string }
431
+ */
365
432
function collectPreText ( node ) {
366
433
return String ( node . value )
367
434
}
368
435
369
- // 3. Every collapsible tab is converted to a collapsible space (U+0020).
370
- // 4. Any collapsible space immediately following another collapsible
371
- // space—even one outside the boundary of the inline containing that
372
- // space, provided both spaces are within the same inline formatting
373
- // context—is collapsed to have zero advance width. (It is invisible,
374
- // but retains its soft wrap opportunity, if any.)
375
- function trimAndcollapseSpacesAndTabs ( value , breakBefore , breakAfter ) {
436
+ /**
437
+ * 3. Every collapsible tab is converted to a collapsible space (U+0020).
438
+ * 4. Any collapsible space immediately following another collapsible
439
+ * space—even one outside the boundary of the inline containing that
440
+ * space, provided both spaces are within the same inline formatting
441
+ * context—is collapsed to have zero advance width. (It is invisible,
442
+ * but retains its soft wrap opportunity, if any.)
443
+ *
444
+ * @param {string } value
445
+ * @param {BreakBefore } breakBefore
446
+ * @param {BreakAfter } breakAfter
447
+ * @returns {string }
448
+ */
449
+ function trimAndCollapseSpacesAndTabs ( value , breakBefore , breakAfter ) {
450
+ /** @type {Array.<string> } */
376
451
var result = [ ]
377
452
var start = 0
453
+ /** @type {RegExpMatchArray } */
378
454
var match
455
+ /** @type {number } */
379
456
var end
380
457
381
458
while ( start < value . length ) {
@@ -406,34 +483,46 @@ function trimAndcollapseSpacesAndTabs(value, breakBefore, breakAfter) {
406
483
return result . join ( ' ' )
407
484
}
408
485
409
- // We don’t support void elements here (so `nobr wbr` -> `normal` is ignored).
486
+ /**
487
+ * We don’t support void elements here (so `nobr wbr` -> `normal` is ignored).
488
+ *
489
+ * @param {HastNode } node
490
+ * @param {CollectionOptions } options
491
+ * @returns {Whitespace }
492
+ */
410
493
function inferWhitespace ( node , options ) {
411
- var props = node . properties || { }
412
- var inherit = options . whitespace || 'normal'
413
-
414
- switch ( node . tagName ) {
415
- case 'listing' :
416
- case 'plaintext' :
417
- case 'xmp' :
418
- return 'pre'
419
- case 'nobr' :
420
- return 'nowrap'
421
- case 'pre' :
422
- return props . wrap ? 'pre-wrap' : 'pre'
423
- case 'td' :
424
- case 'th' :
425
- return props . noWrap ? 'nowrap' : inherit
426
- case 'textarea' :
427
- return 'pre-wrap'
428
- default :
429
- return inherit
494
+ /** @type {HastProperties } */
495
+ var props
496
+
497
+ if ( node . type === 'element' ) {
498
+ props = node . properties || { }
499
+ switch ( node . tagName ) {
500
+ case 'listing' :
501
+ case 'plaintext' :
502
+ case 'xmp' :
503
+ return 'pre'
504
+ case 'nobr' :
505
+ return 'nowrap'
506
+ case 'pre' :
507
+ return props . wrap ? 'pre-wrap' : 'pre'
508
+ case 'td' :
509
+ case 'th' :
510
+ return props . noWrap ? 'nowrap' : options . whitespace
511
+ case 'textarea' :
512
+ return 'pre-wrap'
513
+ default :
514
+ }
430
515
}
516
+
517
+ return options . whitespace
431
518
}
432
519
520
+ /** @type {TestFunctionAnything } */
433
521
function hidden ( node ) {
434
- return ( node . properties || { } ) . hidden
522
+ return Boolean ( ( node . properties || { } ) . hidden )
435
523
}
436
524
525
+ /** @type {TestFunctionAnything } */
437
526
function closedDialog ( node ) {
438
527
return node . tagName === 'dialog' && ! ( node . properties || { } ) . open
439
528
}
0 commit comments