1
+ // To do next major: use `structuredClone` (or so?) to deep clone `properties`
2
+ // and the like: the return value has to be a clone (not shallow copy) of the
3
+ // passed tree.
4
+
1
5
/**
2
6
* @typedef {import('hast').Root } Root
3
7
* @typedef {import('hast').Content } Content
4
8
* @typedef {import('hast').Text } Text
5
- * @typedef {Root|Content } Node
9
+ */
10
+
11
+ /**
12
+ * @typedef {Root | Content } Node
6
13
*
7
14
* @typedef Options
8
15
* Configuration.
9
- * @property {number } [size=140]
16
+ * @property {number | null | undefined } [size=140]
10
17
* Number of characters to truncate to.
11
- * @property {string } [ellipsis]
18
+ * @property {string | null | undefined } [ellipsis]
12
19
* Value to use at truncation point.
13
- * @property {number } [maxCharacterStrip=30]
20
+ * @property {number | null | undefined } [maxCharacterStrip=30]
14
21
* How far to walk back.
15
22
* The algorithm attempts to break right after a word rather than the exact
16
23
* `size`.
25
32
* If `maxCharacterStrip` characters are walked back and no nice break point
26
33
* is found, the bad break point is used.
27
34
* Set `maxCharacterStrip: 0` to not find a nice break.
28
- * @property {Array<Content> } [ignore=[]]
35
+ * @property {Array<Content> | null | undefined } [ignore=[]]
29
36
* Nodes to exclude from the resulting tree.
30
37
* These are not counted towards `size`.
31
38
*/
32
39
33
40
import { unicodeWhitespace , unicodePunctuation } from 'micromark-util-character'
34
41
42
+ /** @type {ReadonlyArray<void> } */
43
+ const empty = [ ]
44
+
35
45
/**
36
46
* Truncate the tree to a certain number of characters.
37
47
*
38
48
* @template {Node} Tree
39
49
* @param {Tree } tree
40
- * @param {Options } [options]
50
+ * @param {Options | null | undefined } [options]
41
51
* @returns {Tree }
52
+ * A shallow copy of `tree`, truncated.
42
53
*/
43
- export function truncate ( tree , options = { } ) {
54
+ export function truncate ( tree , options ) {
44
55
// To do: support units.
45
- const { size = 140 , ellipsis, maxCharacterStrip = 30 , ignore = [ ] } = options
56
+ const config = options || { }
57
+ const size = typeof config . size === 'number' ? config . size : 140
58
+ const maxCharacterStrip =
59
+ typeof config . maxCharacterStrip === 'number' ? config . maxCharacterStrip : 30
60
+ const ignore = config . ignore || empty
61
+ const ellipsis = config . ellipsis
46
62
let searchSize = 0
47
- /** @type {Text| undefined } */
63
+ /** @type {Text | undefined } */
48
64
let overflowingText
65
+
49
66
const result = preorder ( tree )
50
67
51
68
if ( overflowingText ) {
@@ -89,8 +106,12 @@ export function truncate(tree, options = {}) {
89
106
return result
90
107
91
108
/**
109
+ * Transform in `preorder`.
110
+ *
92
111
* @param {Node } node
93
- * @returns {Node|undefined }
112
+ * Node to truncate.
113
+ * @returns {Node }
114
+ * Shallow copy of `node`.
94
115
*/
95
116
function preorder ( node ) {
96
117
if ( node . type === 'text' ) {
@@ -119,6 +140,7 @@ export function truncate(tree, options = {}) {
119
140
if ( result ) children . push ( result )
120
141
}
121
142
143
+ // One of the descendant texts included the breakpoint.
122
144
if ( overflowingText ) {
123
145
break
124
146
}
@@ -134,7 +156,9 @@ export function truncate(tree, options = {}) {
134
156
135
157
/**
136
158
* @param {number } code
159
+ * Character code.
137
160
* @returns {boolean }
161
+ * Whether `code` is not whitespace and not punctuation.
138
162
*/
139
163
function unicodeAlphanumeric ( code ) {
140
164
return ! unicodeWhitespace ( code ) && ! unicodePunctuation ( code )
0 commit comments