Skip to content

Commit c3be8ec

Browse files
committed
Add JSDoc based types
1 parent e9e3685 commit c3be8ec

File tree

5 files changed

+188
-65
lines changed

5 files changed

+188
-65
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
.DS_Store
2+
*.d.ts
23
*.log
34
coverage/
45
node_modules/

index.js

Lines changed: 152 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,27 @@
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+
125
import repeat from 'repeat-string'
226
import {convertElement} from 'hast-util-is-element'
327
import {findAfter} from 'unist-util-find-after'
@@ -73,18 +97,33 @@ var blockOrCaption = convertElement([
7397
'xmp' // Flow content (legacy)
7498
])
7599

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+
*/
80109
export function toText(node) {
110+
/** @type {Array.<HastChild>} */
111+
// @ts-ignore looks like a parent.
81112
var children = node.children || []
82113
var block = blockOrCaption(node)
83-
var whitespace = inferWhitespace(node, {})
114+
var whitespace = inferWhitespace(node, {
115+
whitespace: 'normal',
116+
breakBefore: false,
117+
breakAfter: false
118+
})
84119
var index = -1
120+
/** @type {Array.<string|BreakNumber>} */
85121
var results
122+
/** @type {Array.<string>} */
86123
var result
124+
/** @type {string|BreakNumber} */
87125
var value
126+
/** @type {number} */
88127
var count
89128

90129
// Treat `text` and `comment` as having normal white-space.
@@ -96,11 +135,7 @@ export function toText(node) {
96135
// Nodes without children are treated as a void element, so `doctype` is thus
97136
// ignored.
98137
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})
104139
}
105140

106141
// 1. If this element is not being rendered, or if the user agent is a
@@ -124,7 +159,8 @@ export function toText(node) {
124159
// positive integer (a required line break count).
125160
// 3.2. For each item item in current, append item to results.
126161
results = results.concat(
127-
innerTextCollection(children[index], index, node, {
162+
// @ts-ignore Looks like a parent.
163+
innerTextCollection(children[index], node, {
128164
whitespace,
129165
breakBefore: index ? null : block,
130166
breakAfter:
@@ -159,31 +195,47 @@ export function toText(node) {
159195
return result.join('')
160196
}
161197

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) {
164207
if (node.type === 'element') {
165-
return collectElement(node, index, parent, options)
208+
return collectElement(node, parent, options)
166209
}
167210

168211
if (node.type === 'text') {
169212
return [
170213
options.whitespace === 'normal'
171214
? collectText(node, options)
172-
: collectPreText(node, options)
215+
: collectPreText(node)
173216
]
174217
}
175218

176219
return []
177220
}
178221

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) {
181230
// First we infer the `white-space` property.
182231
var whitespace = inferWhitespace(node, options)
183232
var children = node.children || []
184233
var index = -1
234+
/** @type {Array.<string|BreakNumber>} */
185235
var items = []
236+
/** @type {BreakNumber} */
186237
var prefix
238+
/** @type {BreakNumber|BreakForce} */
187239
var suffix
188240

189241
// We’re ignoring point 3, and exiting without any content here, because we
@@ -244,9 +296,9 @@ function collectElement(node, _, parent, options) {
244296
// results to a single list.
245297
while (++index < children.length) {
246298
items = items.concat(
247-
innerTextCollection(children[index], index, node, {
299+
innerTextCollection(children[index], node, {
248300
whitespace,
249-
breakBefore: index ? null : prefix,
301+
breakBefore: index ? undefined : prefix,
250302
breakAfter:
251303
index < children.length - 1 ? br(children[index + 1]) : suffix
252304
})
@@ -270,29 +322,40 @@ function collectElement(node, _, parent, options) {
270322
return items
271323
}
272324

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+
*/
288346
function collectText(node, options) {
289347
var value = String(node.value)
348+
/** @type {Array.<string>} */
290349
var lines = []
350+
/** @type {Array.<string>} */
291351
var result = []
292352
var start = 0
293353
var index = -1
354+
/** @type {RegExpMatchArray} */
294355
var match
356+
/** @type {number} */
295357
var end
358+
/** @type {string} */
296359
var join
297360

298361
while (start < value.length) {
@@ -303,7 +366,7 @@ function collectText(node, options) {
303366
lines.push(
304367
// Any sequence of collapsible spaces and tabs immediately preceding or
305368
// following a segment break is removed.
306-
trimAndcollapseSpacesAndTabs(
369+
trimAndCollapseSpacesAndTabs(
307370
// [...] ignoring bidi formatting characters (characters with the
308371
// Bidi_Control property [UAX9]: ALM, LTR, RTL, LRE-RLO, LRI-PDI) as if
309372
// they were not there.
@@ -362,20 +425,34 @@ function collectText(node, options) {
362425
return result.join('')
363426
}
364427

428+
/**
429+
* @param {HastText|HastComment} node
430+
* @returns {string}
431+
*/
365432
function collectPreText(node) {
366433
return String(node.value)
367434
}
368435

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>} */
376451
var result = []
377452
var start = 0
453+
/** @type {RegExpMatchArray} */
378454
var match
455+
/** @type {number} */
379456
var end
380457

381458
while (start < value.length) {
@@ -406,34 +483,46 @@ function trimAndcollapseSpacesAndTabs(value, breakBefore, breakAfter) {
406483
return result.join(' ')
407484
}
408485

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+
*/
410493
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+
}
430515
}
516+
517+
return options.whitespace
431518
}
432519

520+
/** @type {TestFunctionAnything} */
433521
function hidden(node) {
434-
return (node.properties || {}).hidden
522+
return Boolean((node.properties || {}).hidden)
435523
}
436524

525+
/** @type {TestFunctionAnything} */
437526
function closedDialog(node) {
438527
return node.tagName === 'dialog' && !(node.properties || {}).open
439528
}

package.json

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,29 +28,39 @@
2828
"sideEffects": false,
2929
"type": "module",
3030
"main": "index.js",
31+
"types": "index.d.ts",
3132
"files": [
33+
"index.d.ts",
3234
"index.js"
3335
],
3436
"dependencies": {
37+
"@types/hast": "^2.0.0",
38+
"@types/repeat-string": "^1.0.0",
3539
"hast-util-is-element": "^2.0.0",
3640
"repeat-string": "^1.0.0",
3741
"unist-util-find-after": "^4.0.0"
3842
},
3943
"devDependencies": {
40-
"c8": "^7.7.1",
44+
"@types/tape": "^4.0.0",
45+
"c8": "^7.0.0",
4146
"hastscript": "^7.0.0",
4247
"prettier": "^2.0.0",
4348
"remark-cli": "^9.0.0",
4449
"remark-preset-wooorm": "^8.0.0",
50+
"rimraf": "^3.0.0",
4551
"tape": "^5.0.0",
52+
"type-coverage": "^2.0.0",
53+
"typescript": "^4.0.0",
4654
"unist-builder": "^3.0.0",
4755
"xo": "^0.39.0"
4856
},
4957
"scripts": {
58+
"prepack": "npm run build && npm run format",
59+
"build": "rimraf \"*.d.ts\" && tsc && type-coverage",
5060
"format": "remark . -qfo && prettier . -w --loglevel warn && xo --fix",
5161
"test-api": "node test.js",
5262
"test-coverage": "c8 --check-coverage --branches 100 --functions 100 --lines 100 --statements 100 --reporter lcov node test.js",
53-
"test": "npm run format && npm run test-coverage"
63+
"test": "npm run build && npm run format && npm run test-coverage"
5464
},
5565
"prettier": {
5666
"tabWidth": 2,
@@ -71,5 +81,10 @@
7181
"plugins": [
7282
"preset-wooorm"
7383
]
84+
},
85+
"typeCoverage": {
86+
"atLeast": 100,
87+
"detail": true,
88+
"strict": true
7489
}
7590
}

0 commit comments

Comments
 (0)