1
1
/**
2
2
* @typedef {import('hast').Element } Element
3
3
* @typedef {import('hast').Nodes } Nodes
4
- * @typedef {import('hast').Root } Root
5
4
* @typedef {import('hast').RootContent } RootContent
5
+ * @typedef {import('hast').Root } Root
6
6
*
7
7
* @typedef {import('property-information').Info } Info
8
8
* @typedef {import('property-information').Schema } Schema
9
9
*/
10
10
11
+ /**
12
+ * @typedef {Array<Nodes | PrimitiveChild> } ArrayChildNested
13
+ * List of children (deep).
14
+ */
15
+
16
+ /**
17
+ * @typedef {Array<ArrayChildNested | Nodes | PrimitiveChild> } ArrayChild
18
+ * List of children.
19
+ */
20
+
21
+ /**
22
+ * @typedef {Array<number | string> } ArrayValue
23
+ * List of property values for space- or comma separated values (such as `className`).
24
+ */
25
+
26
+ /**
27
+ * @typedef {ArrayChild | Nodes | PrimitiveChild } Child
28
+ * Acceptable child value.
29
+ */
30
+
31
+ /**
32
+ * @typedef {number | string | null | undefined } PrimitiveChild
33
+ * Primitive children, either ignored (nullish), or turned into text nodes.
34
+ */
35
+
36
+ /**
37
+ * @typedef {boolean | number | string | null | undefined } PrimitiveValue
38
+ * Primitive property value.
39
+ */
40
+
41
+ /**
42
+ * @typedef {Record<string, PropertyValue | Style> } Properties
43
+ * Acceptable value for element properties.
44
+ */
45
+
46
+ /**
47
+ * @typedef {ArrayValue | PrimitiveValue } PropertyValue
48
+ * Primitive value or list value.
49
+ */
50
+
11
51
/**
12
52
* @typedef {Element | Root } Result
13
53
* Result from a `h` (or `s`) call.
14
- *
54
+ */
55
+
56
+ /**
15
57
* @typedef {number | string } StyleValue
16
58
* Value for a CSS style field.
59
+ */
60
+
61
+ /**
17
62
* @typedef {Record<string, StyleValue> } Style
18
63
* Supported value of a `style` prop.
19
- * @typedef {boolean | number | string | null | undefined } PrimitiveValue
20
- * Primitive property value.
21
- * @typedef {Array<number | string> } ArrayValue
22
- * List of property values for space- or comma separated values (such as `className`).
23
- * @typedef {ArrayValue | PrimitiveValue } PropertyValue
24
- * Primitive value or list value.
25
- * @typedef {{[property: string]: PropertyValue | Style} } Properties
26
- * Acceptable value for element properties.
27
- *
28
- * @typedef {number | string | null | undefined } PrimitiveChild
29
- * Primitive children, either ignored (nullish), or turned into text nodes.
30
- * @typedef {Array<ArrayChildNested | Nodes | PrimitiveChild> } ArrayChild
31
- * List of children.
32
- * @typedef {Array<Nodes | PrimitiveChild> } ArrayChildNested
33
- * List of children (deep).
34
- * @typedef {ArrayChild | Nodes | PrimitiveChild } Child
35
- * Acceptable child value.
36
64
*/
37
65
38
- import { parse as commas } from 'comma-separated-tokens'
66
+ import { parse as parseCommas } from 'comma-separated-tokens'
39
67
import { parseSelector } from 'hast-util-parse-selector'
40
68
import { find , normalize } from 'property-information'
41
- import { parse as spaces } from 'space-separated-tokens'
42
-
43
- const own = { } . hasOwnProperty
69
+ import { parse as parseSpaces } from 'space-separated-tokens'
44
70
45
71
/**
46
72
* @param {Schema } schema
47
73
* Schema to use.
48
74
* @param {string } defaultTagName
49
75
* Default tag name.
50
- * @param {Array <string> | undefined } [caseSensitive]
76
+ * @param {ReadonlyArray <string> | undefined } [caseSensitive]
51
77
* Case-sensitive tag names (default: `undefined`).
52
78
* @returns
53
79
* `h`.
54
80
*/
55
81
export function createH ( schema , defaultTagName , caseSensitive ) {
56
- const adjust = caseSensitive && createAdjustMap ( caseSensitive )
82
+ const adjust = caseSensitive ? createAdjustMap ( caseSensitive ) : undefined
57
83
58
84
/**
59
85
* Hyperscript compatible DSL for creating virtual hast trees.
@@ -84,41 +110,34 @@ export function createH(schema, defaultTagName, caseSensitive) {
84
110
* Result.
85
111
*/
86
112
function h ( selector , properties , ...children ) {
87
- let index = - 1
88
113
/** @type {Result } */
89
114
let node
90
115
91
- if ( selector === undefined || selector === null ) {
116
+ if ( selector === null || selector === undefined ) {
92
117
node = { type : 'root' , children : [ ] }
93
118
// Properties are not supported for roots.
94
119
const child = /** @type {Child } */ ( properties )
95
120
children . unshift ( child )
96
121
} else {
97
122
node = parseSelector ( selector , defaultTagName )
98
123
// Normalize the name.
99
- node . tagName = node . tagName . toLowerCase ( )
100
- if ( adjust && own . call ( adjust , node . tagName ) ) {
101
- node . tagName = adjust [ node . tagName ]
102
- }
124
+ const lower = node . tagName . toLowerCase ( )
125
+ const adjusted = adjust ? adjust . get ( lower ) : undefined
126
+ node . tagName = adjusted || lower
103
127
104
128
// Handle properties.
105
129
if ( isChild ( properties ) ) {
106
130
children . unshift ( properties )
107
131
} else {
108
- /** @type {string } */
109
- let key
110
-
111
- for ( key in properties ) {
112
- if ( own . call ( properties , key ) ) {
113
- addProperty ( schema , node . properties , key , properties [ key ] )
114
- }
132
+ for ( const [ key , value ] of Object . entries ( properties ) ) {
133
+ addProperty ( schema , node . properties , key , value )
115
134
}
116
135
}
117
136
}
118
137
119
138
// Handle children.
120
- while ( ++ index < children . length ) {
121
- addChild ( node . children , children [ index ] )
139
+ for ( const child of children ) {
140
+ addChild ( node . children , child )
122
141
}
123
142
124
143
if ( node . type === 'element' && node . tagName === 'template' ) {
@@ -160,7 +179,7 @@ function isChild(value) {
160
179
if ( value && typeof value === 'object' ) {
161
180
if ( ! Array . isArray ( value ) ) return true
162
181
163
- const list = /** @type {Array <unknown> } */ ( value )
182
+ const list = /** @type {ReadonlyArray <unknown> } */ ( value )
164
183
165
184
for ( const item of list ) {
166
185
if ( typeof item !== 'number' && typeof item !== 'string' ) {
@@ -195,12 +214,11 @@ function isChild(value) {
195
214
*/
196
215
function addProperty ( schema , properties , key , value ) {
197
216
const info = find ( schema , key )
198
- let index = - 1
199
217
/** @type {PropertyValue } */
200
218
let result
201
219
202
220
// Ignore nullish and NaN values.
203
- if ( value === undefined || value === null ) return
221
+ if ( value === null || value === undefined ) return
204
222
205
223
if ( typeof value === 'number' ) {
206
224
// Ignore NaN.
@@ -215,16 +233,16 @@ function addProperty(schema, properties, key, value) {
215
233
// Handle list values.
216
234
else if ( typeof value === 'string' ) {
217
235
if ( info . spaceSeparated ) {
218
- result = spaces ( value )
236
+ result = parseSpaces ( value )
219
237
} else if ( info . commaSeparated ) {
220
- result = commas ( value )
238
+ result = parseCommas ( value )
221
239
} else if ( info . commaOrSpaceSeparated ) {
222
- result = spaces ( commas ( value ) . join ( ' ' ) )
240
+ result = parseSpaces ( parseCommas ( value ) . join ( ' ' ) )
223
241
} else {
224
242
result = parsePrimitive ( info , info . property , value )
225
243
}
226
244
} else if ( Array . isArray ( value ) ) {
227
- result = value . concat ( )
245
+ result = [ ... value ]
228
246
} else {
229
247
result = info . property === 'style' ? style ( value ) : String ( value )
230
248
}
@@ -233,12 +251,13 @@ function addProperty(schema, properties, key, value) {
233
251
/** @type {Array<number | string> } */
234
252
const finalResult = [ ]
235
253
236
- while ( ++ index < result . length ) {
254
+ for ( const item of result ) {
237
255
// Assume no booleans in array.
238
- const value = /** @type {number | string } */ (
239
- parsePrimitive ( info , info . property , result [ index ] )
256
+ finalResult . push (
257
+ /** @type {number | string } */ (
258
+ parsePrimitive ( info , info . property , item )
259
+ )
240
260
)
241
- finalResult [ index ] = value
242
261
}
243
262
244
263
result = finalResult
@@ -247,8 +266,9 @@ function addProperty(schema, properties, key, value) {
247
266
// Class names (which can be added both on the `selector` and here).
248
267
if ( info . property === 'className' && Array . isArray ( properties . className ) ) {
249
268
// Assume no booleans in `className`.
250
- const value = /** @type {number | string } */ ( result )
251
- result = properties . className . concat ( value )
269
+ result = properties . className . concat (
270
+ /** @type {Array<number | string> | number | string } */ ( result )
271
+ )
252
272
}
253
273
254
274
properties [ info . property ] = result
@@ -263,15 +283,13 @@ function addProperty(schema, properties, key, value) {
263
283
* Nothing.
264
284
*/
265
285
function addChild ( nodes , value ) {
266
- let index = - 1
267
-
268
- if ( value === undefined || value === null ) {
286
+ if ( value === null || value === undefined ) {
269
287
// Empty.
270
- } else if ( typeof value === 'string ' || typeof value === 'number ' ) {
288
+ } else if ( typeof value === 'number ' || typeof value === 'string ' ) {
271
289
nodes . push ( { type : 'text' , value : String ( value ) } )
272
290
} else if ( Array . isArray ( value ) ) {
273
- while ( ++ index < value . length ) {
274
- addChild ( nodes , value [ index ] )
291
+ for ( const child of value ) {
292
+ addChild ( nodes , child )
275
293
}
276
294
} else if ( typeof value === 'object' && 'type' in value ) {
277
295
if ( value . type === 'root' ) {
@@ -316,21 +334,17 @@ function parsePrimitive(info, name, value) {
316
334
/**
317
335
* Serialize a `style` object as a string.
318
336
*
319
- * @param {Style } value
337
+ * @param {Style } styles
320
338
* Style object.
321
339
* @returns {string }
322
340
* CSS string.
323
341
*/
324
- function style ( value ) {
342
+ function style ( styles ) {
325
343
/** @type {Array<string> } */
326
344
const result = [ ]
327
- /** @type {string } */
328
- let key
329
345
330
- for ( key in value ) {
331
- if ( own . call ( value , key ) ) {
332
- result . push ( [ key , value [ key ] ] . join ( ': ' ) )
333
- }
346
+ for ( const [ key , value ] of Object . entries ( styles ) ) {
347
+ result . push ( [ key , value ] . join ( ': ' ) )
334
348
}
335
349
336
350
return result . join ( '; ' )
@@ -339,18 +353,17 @@ function style(value) {
339
353
/**
340
354
* Create a map to adjust casing.
341
355
*
342
- * @param {Array <string> } values
356
+ * @param {ReadonlyArray <string> } values
343
357
* List of properly cased keys.
344
- * @returns {Record <string, string> }
358
+ * @returns {Map <string, string> }
345
359
* Map of lowercase keys to uppercase keys.
346
360
*/
347
361
function createAdjustMap ( values ) {
348
- /** @type {Record<string, string> } */
349
- const result = { }
350
- let index = - 1
362
+ /** @type {Map<string, string> } */
363
+ const result = new Map ( )
351
364
352
- while ( ++ index < values . length ) {
353
- result [ values [ index ] . toLowerCase ( ) ] = values [ index ]
365
+ for ( const value of values ) {
366
+ result . set ( value . toLowerCase ( ) , value )
354
367
}
355
368
356
369
return result
0 commit comments