11/**
22 * @typedef {import('hast').Element } Element
33 * @typedef {import('hast').Nodes } Nodes
4- * @typedef {import('hast').Root } Root
54 * @typedef {import('hast').RootContent } RootContent
5+ * @typedef {import('hast').Root } Root
66 *
77 * @typedef {import('property-information').Info } Info
88 * @typedef {import('property-information').Schema } Schema
99 */
1010
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+
1151/**
1252 * @typedef {Element | Root } Result
1353 * Result from a `h` (or `s`) call.
14- *
54+ */
55+
56+ /**
1557 * @typedef {number | string } StyleValue
1658 * Value for a CSS style field.
59+ */
60+
61+ /**
1762 * @typedef {Record<string, StyleValue> } Style
1863 * 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.
3664 */
3765
38- import { parse as commas } from 'comma-separated-tokens'
66+ import { parse as parseCommas } from 'comma-separated-tokens'
3967import { parseSelector } from 'hast-util-parse-selector'
4068import { 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'
4470
4571/**
4672 * @param {Schema } schema
4773 * Schema to use.
4874 * @param {string } defaultTagName
4975 * Default tag name.
50- * @param {Array <string> | undefined } [caseSensitive]
76+ * @param {ReadonlyArray <string> | undefined } [caseSensitive]
5177 * Case-sensitive tag names (default: `undefined`).
5278 * @returns
5379 * `h`.
5480 */
5581export function createH ( schema , defaultTagName , caseSensitive ) {
56- const adjust = caseSensitive && createAdjustMap ( caseSensitive )
82+ const adjust = caseSensitive ? createAdjustMap ( caseSensitive ) : undefined
5783
5884 /**
5985 * Hyperscript compatible DSL for creating virtual hast trees.
@@ -84,41 +110,34 @@ export function createH(schema, defaultTagName, caseSensitive) {
84110 * Result.
85111 */
86112 function h ( selector , properties , ...children ) {
87- let index = - 1
88113 /** @type {Result } */
89114 let node
90115
91- if ( selector === undefined || selector === null ) {
116+ if ( selector === null || selector === undefined ) {
92117 node = { type : 'root' , children : [ ] }
93118 // Properties are not supported for roots.
94119 const child = /** @type {Child } */ ( properties )
95120 children . unshift ( child )
96121 } else {
97122 node = parseSelector ( selector , defaultTagName )
98123 // 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
103127
104128 // Handle properties.
105129 if ( isChild ( properties ) ) {
106130 children . unshift ( properties )
107131 } 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 )
115134 }
116135 }
117136 }
118137
119138 // Handle children.
120- while ( ++ index < children . length ) {
121- addChild ( node . children , children [ index ] )
139+ for ( const child of children ) {
140+ addChild ( node . children , child )
122141 }
123142
124143 if ( node . type === 'element' && node . tagName === 'template' ) {
@@ -160,7 +179,7 @@ function isChild(value) {
160179 if ( value && typeof value === 'object' ) {
161180 if ( ! Array . isArray ( value ) ) return true
162181
163- const list = /** @type {Array <unknown> } */ ( value )
182+ const list = /** @type {ReadonlyArray <unknown> } */ ( value )
164183
165184 for ( const item of list ) {
166185 if ( typeof item !== 'number' && typeof item !== 'string' ) {
@@ -195,12 +214,11 @@ function isChild(value) {
195214 */
196215function addProperty ( schema , properties , key , value ) {
197216 const info = find ( schema , key )
198- let index = - 1
199217 /** @type {PropertyValue } */
200218 let result
201219
202220 // Ignore nullish and NaN values.
203- if ( value === undefined || value === null ) return
221+ if ( value === null || value === undefined ) return
204222
205223 if ( typeof value === 'number' ) {
206224 // Ignore NaN.
@@ -215,16 +233,16 @@ function addProperty(schema, properties, key, value) {
215233 // Handle list values.
216234 else if ( typeof value === 'string' ) {
217235 if ( info . spaceSeparated ) {
218- result = spaces ( value )
236+ result = parseSpaces ( value )
219237 } else if ( info . commaSeparated ) {
220- result = commas ( value )
238+ result = parseCommas ( value )
221239 } else if ( info . commaOrSpaceSeparated ) {
222- result = spaces ( commas ( value ) . join ( ' ' ) )
240+ result = parseSpaces ( parseCommas ( value ) . join ( ' ' ) )
223241 } else {
224242 result = parsePrimitive ( info , info . property , value )
225243 }
226244 } else if ( Array . isArray ( value ) ) {
227- result = value . concat ( )
245+ result = [ ... value ]
228246 } else {
229247 result = info . property === 'style' ? style ( value ) : String ( value )
230248 }
@@ -233,12 +251,13 @@ function addProperty(schema, properties, key, value) {
233251 /** @type {Array<number | string> } */
234252 const finalResult = [ ]
235253
236- while ( ++ index < result . length ) {
254+ for ( const item of result ) {
237255 // 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+ )
240260 )
241- finalResult [ index ] = value
242261 }
243262
244263 result = finalResult
@@ -247,8 +266,9 @@ function addProperty(schema, properties, key, value) {
247266 // Class names (which can be added both on the `selector` and here).
248267 if ( info . property === 'className' && Array . isArray ( properties . className ) ) {
249268 // 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+ )
252272 }
253273
254274 properties [ info . property ] = result
@@ -263,15 +283,13 @@ function addProperty(schema, properties, key, value) {
263283 * Nothing.
264284 */
265285function addChild ( nodes , value ) {
266- let index = - 1
267-
268- if ( value === undefined || value === null ) {
286+ if ( value === null || value === undefined ) {
269287 // Empty.
270- } else if ( typeof value === 'string ' || typeof value === 'number ' ) {
288+ } else if ( typeof value === 'number ' || typeof value === 'string ' ) {
271289 nodes . push ( { type : 'text' , value : String ( value ) } )
272290 } 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 )
275293 }
276294 } else if ( typeof value === 'object' && 'type' in value ) {
277295 if ( value . type === 'root' ) {
@@ -316,21 +334,17 @@ function parsePrimitive(info, name, value) {
316334/**
317335 * Serialize a `style` object as a string.
318336 *
319- * @param {Style } value
337+ * @param {Style } styles
320338 * Style object.
321339 * @returns {string }
322340 * CSS string.
323341 */
324- function style ( value ) {
342+ function style ( styles ) {
325343 /** @type {Array<string> } */
326344 const result = [ ]
327- /** @type {string } */
328- let key
329345
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 ( ': ' ) )
334348 }
335349
336350 return result . join ( '; ' )
@@ -339,18 +353,17 @@ function style(value) {
339353/**
340354 * Create a map to adjust casing.
341355 *
342- * @param {Array <string> } values
356+ * @param {ReadonlyArray <string> } values
343357 * List of properly cased keys.
344- * @returns {Record <string, string> }
358+ * @returns {Map <string, string> }
345359 * Map of lowercase keys to uppercase keys.
346360 */
347361function createAdjustMap ( values ) {
348- /** @type {Record<string, string> } */
349- const result = { }
350- let index = - 1
362+ /** @type {Map<string, string> } */
363+ const result = new Map ( )
351364
352- while ( ++ index < values . length ) {
353- result [ values [ index ] . toLowerCase ( ) ] = values [ index ]
365+ for ( const value of values ) {
366+ result . set ( value . toLowerCase ( ) , value )
354367 }
355368
356369 return result
0 commit comments