1
+ /**
2
+ * @typedef {import('hast').Element } Element
3
+ * @typedef {import('hast').Root } Root
4
+ * @typedef {import('hast').Text } Text
5
+ *
6
+ * @typedef {import('unist-util-is').AssertPredicate<Element> } AssertElement
7
+ * @typedef {import('unist-util-is').AssertPredicate<Text> } AssertText
8
+ * @typedef {import('unist-util-is').AssertPredicate<Root> } AssertRoot
9
+ *
10
+ * @callback CreateElementLike
11
+ * @param {string } name
12
+ * @param {Object<string, any> } [attributes]
13
+ * @param {Array.<string|any> } [children]
14
+ * @returns {any }
15
+ *
16
+ * @typedef Context
17
+ * @property {html|svg } schema
18
+ * @property {string|null } prefix
19
+ * @property {number } key
20
+ * @property {boolean } react
21
+ * @property {boolean } vue
22
+ * @property {boolean } vdom
23
+ * @property {boolean } hyperscript
24
+ *
25
+ * @typedef Options
26
+ * @property {string|null } [prefix]
27
+ * @property {'html'|'svg' } [space]
28
+ */
29
+
1
30
import { html , svg , find , hastToReact } from 'property-information'
2
31
import { stringify as spaces } from 'space-separated-tokens'
3
32
import { stringify as commas } from 'comma-separated-tokens'
@@ -7,52 +36,76 @@ import {convert} from 'unist-util-is'
7
36
8
37
var own = { } . hasOwnProperty
9
38
39
+ /** @type {AssertRoot } */
40
+ // @ts -ignore it’s correct.
10
41
var root = convert ( 'root' )
42
+ /** @type {AssertElement } */
43
+ // @ts -ignore it’s correct.
11
44
var element = convert ( 'element' )
45
+ /** @type {AssertText } */
46
+ // @ts -ignore it’s correct.
12
47
var text = convert ( 'text' )
13
48
14
- export function toH ( h , node , options ) {
15
- var settings = options || { }
49
+ /**
50
+ * @template {CreateElementLike} H
51
+ * @param {H } h
52
+ * @param {Element|Root } tree
53
+ * @param {string|boolean|Options } [options]
54
+ * @returns {ReturnType<H> }
55
+ */
56
+ export function toH ( h , tree , options ) {
57
+ if ( typeof h !== 'function' ) {
58
+ throw new TypeError ( 'h is not a function' )
59
+ }
60
+
16
61
var r = react ( h )
17
62
var v = vue ( h )
18
63
var vd = vdom ( h )
64
+ /** @type {string|boolean } */
19
65
var prefix
66
+ /** @type {Element } */
67
+ var node
20
68
21
- if ( typeof h !== 'function' ) {
22
- throw new TypeError ( 'h is not a function' )
23
- }
24
-
25
- if ( typeof settings === 'string' || typeof settings === 'boolean' ) {
26
- prefix = settings
27
- settings = { }
69
+ if ( typeof options === 'string' || typeof options === 'boolean' ) {
70
+ prefix = options
71
+ options = { }
28
72
} else {
29
- prefix = settings . prefix
73
+ if ( ! options ) options = { }
74
+ prefix = options . prefix
30
75
}
31
76
32
- if ( root ( node ) ) {
77
+ if ( root ( tree ) ) {
78
+ // @ts -ignore Allow `doctypes` in there, we’ll filter them out later.
33
79
node =
34
- node . children . length === 1 && element ( node . children [ 0 ] )
35
- ? node . children [ 0 ]
80
+ tree . children . length === 1 && element ( tree . children [ 0 ] )
81
+ ? tree . children [ 0 ]
36
82
: {
37
83
type : 'element' ,
38
84
tagName : 'div' ,
39
85
properties : { } ,
40
- children : node . children
86
+ children : tree . children
41
87
}
42
- } else if ( ! element ( node ) ) {
88
+ } else if ( element ( tree ) ) {
89
+ node = tree
90
+ } else {
43
91
throw new Error (
44
- 'Expected root or element, not `' + ( ( node && node . type ) || node ) + '`'
92
+ // @ts -ignore runtime.
93
+ 'Expected root or element, not `' + ( ( tree && tree . type ) || tree ) + '`'
45
94
)
46
95
}
47
96
48
97
return transform ( h , node , {
49
- schema : settings . space === 'svg' ? svg : html ,
98
+ schema : options . space === 'svg' ? svg : html ,
50
99
prefix :
51
100
prefix === undefined || prefix === null
52
101
? r || v || vd
53
102
? 'h-'
54
103
: null
55
- : prefix ,
104
+ : typeof prefix === 'string'
105
+ ? prefix
106
+ : prefix
107
+ ? 'h-'
108
+ : null ,
56
109
key : 0 ,
57
110
react : r ,
58
111
vue : v ,
@@ -61,15 +114,26 @@ export function toH(h, node, options) {
61
114
} )
62
115
}
63
116
64
- // Transform a hast node through a hyperscript interface to *anything*!
117
+ /**
118
+ * Transform a hast node through a hyperscript interface to *anything*!
119
+ *
120
+ * @template {CreateElementLike} H
121
+ * @param {H } h
122
+ * @param {Element } node
123
+ * @param {Context } ctx
124
+ */
65
125
function transform ( h , node , ctx ) {
66
126
var parentSchema = ctx . schema
67
127
var schema = parentSchema
68
128
var name = node . tagName
129
+ /** @type {Object.<string, unknown> } */
69
130
var attributes = { }
131
+ /** @type {Array.<ReturnType<H>|string> } */
70
132
var nodes = [ ]
71
133
var index = - 1
134
+ /** @type {string } */
72
135
var key
136
+ /** @type {Element['children'][number] } */
73
137
var value
74
138
75
139
if ( parentSchema . space === 'html' && name . toLowerCase ( ) === 'svg' ) {
@@ -118,9 +182,17 @@ function transform(h, node, ctx) {
118
182
: h . call ( node , name , attributes )
119
183
}
120
184
185
+ /**
186
+ * @param {Object.<string, unknown> } props
187
+ * @param {string } prop
188
+ * @param {unknown } value
189
+ * @param {Context } ctx
190
+ * @param {string } name
191
+ */
121
192
// eslint-disable-next-line complexity, max-params
122
193
function addAttribute ( props , prop , value , ctx , name ) {
123
194
var info = find ( ctx . schema , prop )
195
+ /** @type {string } */
124
196
var subprop
125
197
126
198
// Ignore nullish and `NaN` values.
@@ -135,7 +207,7 @@ function addAttribute(props, prop, value, ctx, name) {
135
207
return
136
208
}
137
209
138
- if ( value && typeof value === 'object' && 'length' in value ) {
210
+ if ( Array . isArray ( value ) ) {
139
211
// Accept `array`.
140
212
// Most props are space-separated.
141
213
value = info . commaSeparated ? commas ( value ) : spaces ( value )
@@ -175,32 +247,67 @@ function addAttribute(props, prop, value, ctx, name) {
175
247
}
176
248
}
177
249
178
- // Check if `h` is `react.createElement`.
250
+ /**
251
+ * Check if `h` is `react.createElement`.
252
+ *
253
+ * @param {CreateElementLike } h
254
+ * @returns {boolean }
255
+ */
179
256
function react ( h ) {
180
- var node = h && h ( 'div' )
257
+ /** @type {unknown } */
258
+ var node = h ( 'div' )
181
259
return Boolean (
182
260
node &&
261
+ // @ts -ignore Looks like a React node.
183
262
( '_owner' in node || '_store' in node ) &&
263
+ // @ts -ignore Looks like a React node.
184
264
( node . key === undefined || node . key === null )
185
265
)
186
266
}
187
267
188
- // Check if `h` is `hyperscript`.
268
+ /**
269
+ * Check if `h` is `hyperscript`.
270
+ *
271
+ * @param {CreateElementLike } h
272
+ * @returns {boolean }
273
+ */
189
274
function hyperscript ( h ) {
190
- return Boolean ( h && h . context && h . cleanup )
275
+ return 'context' in h && ' cleanup' in h
191
276
}
192
277
193
- // Check if `h` is `virtual-dom/h`.
278
+ /**
279
+ * Check if `h` is `virtual-dom/h`.
280
+ *
281
+ * @param {CreateElementLike } h
282
+ * @returns {boolean }
283
+ */
194
284
function vdom ( h ) {
195
- return h && h ( 'div' ) . type === 'VirtualNode'
285
+ /** @type {unknown } */
286
+ var node = h ( 'div' )
287
+ // @ts -ignore Looks like a vnode.
288
+ return node . type === 'VirtualNode'
196
289
}
197
290
291
+ /**
292
+ * Check if `h` is Vue.
293
+ *
294
+ * @param {CreateElementLike } h
295
+ * @returns {boolean }
296
+ */
198
297
function vue ( h ) {
199
- var node = h && h ( 'div' )
298
+ /** @type {unknown } */
299
+ var node = h ( 'div' )
300
+ // @ts -ignore Looks like a Vue node.
200
301
return Boolean ( node && node . context && node . context . _isVue )
201
302
}
202
303
304
+ /**
305
+ * @param {string } value
306
+ * @param {string } tagName
307
+ * @returns {Object.<string, string> }
308
+ */
203
309
function parseStyle ( value , tagName ) {
310
+ /** @type {Object.<string, string> } */
204
311
var result = { }
205
312
206
313
try {
@@ -213,12 +320,22 @@ function parseStyle(value, tagName) {
213
320
214
321
return result
215
322
323
+ /**
324
+ * @param {string } name
325
+ * @param {string } value
326
+ * @returns {void }
327
+ */
216
328
function iterator ( name , value ) {
217
329
if ( name . slice ( 0 , 4 ) === '-ms-' ) name = 'ms-' + name . slice ( 4 )
218
330
result [ name . replace ( / - ( [ a - z ] ) / g, styleReplacer ) ] = value
219
331
}
220
332
}
221
333
222
- function styleReplacer ( $0 , $1 ) {
334
+ /**
335
+ * @param {string } _
336
+ * @param {string } $1
337
+ * @returns {string }
338
+ */
339
+ function styleReplacer ( _ , $1 ) {
223
340
return $1 . toUpperCase ( )
224
341
}
0 commit comments