@jackens/nnn 2026.4.13 → 2026.4.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/nnn.d.ts +213 -104
  2. package/nnn.js +17 -17
  3. package/package.json +1 -1
  4. package/readme.md +670 -347
package/readme.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # nnn
2
2
 
3
- A collection of Jackens’ JavaScript helper utilities (version: `2026.4.13`).
3
+ A collection of Jackens’ JavaScript helper utilities (version: `2026.4.15`).
4
4
 
5
5
  ## Installation
6
6
 
@@ -41,40 +41,95 @@ import {
41
41
  s,
42
42
  svgUse,
43
43
  uuidV1,
44
- vivify
44
+ vivify,
45
45
  } from '@jackens/nnn' // or './node_modules/@jackens/nnn/nnn.js'
46
46
  ```
47
47
 
48
48
  ## Exports
49
49
 
50
- - [`CNode`](#CNode): Represents a CSS rule node for the [`c`](#c) helper. Keys are CSS properties or nested selectors.
51
- - [`CRoot`](#CRoot): Represents the root CSS object for the [`c`](#c) helper. Keys are top-level selectors or at-rules.
52
- - [`HArgs`](#HArgs): Tuple argument type for the [`h`](#h) and [`s`](#s) helpers.
53
- - [`HArgs1`](#HArgs1): Single argument type for the [`h`](#h) and [`s`](#s) helpers.
54
- - [`c`](#c): A minimal CSS-in-JS helper that converts a JavaScript object hierarchy into a CSS string.
55
- - [`csvParse`](#csvParse): Parses a CSV string into a two-dimensional array of strings.
56
- - [`fixPlTypography`](#fixPlTypography): Applies Polish-specific typographic corrections to a DOM subtree.
57
- - [`h`](#h): A lightweight [HyperScript](https://github.com/hyperhype/hyperscript)-style helper for creating and modifying `HTMLElement`s (see also [`s`](#s)).
58
- - [`hasOwn`](#hasOwn): Checks whether an object has the specified key as its own property.
59
- - [`isArray`](#isArray): Checks whether the argument is an array.
60
- - [`isFiniteNumber`](#isFiniteNumber): Checks whether the argument is a finite number (excludes `±Infinity` and `NaN`).
61
- - [`isInteger`](#isInteger): Checks whether the argument is an integer number.
62
- - [`isNumber`](#isNumber): Checks whether the argument is of type `number` (includes `NaN` and `±Infinity`).
63
- - [`isRecord`](#isRecord): Checks whether the argument is a plain object (not `null` and not an array).
64
- - [`isString`](#isString): Checks whether the argument is a string.
65
- - [`jsOnParse`](#jsOnParse): Parses JSON with support for handler-based value transformation (“JavaScript ON”).
66
- - [`monokai`](#monokai): A Monokai-inspired color scheme for use with the [`c`](#c) helper and [`nanolightTs`](#nanolightTs) tokenizer.
67
- - [`nanolightTs`](#nanolightTs): A TypeScript/JavaScript syntax highlighting tokenizer built using [`newTokenizer`](#newTokenizer).
68
- - [`newEscape`](#newEscape): Creates a tag function for escaping interpolated values in template literals.
69
- - [`newNounForm`](#newNounForm): Creates a function that returns the appropriate noun form based on a numeric value using `Intl.PluralRules`.
70
- - [`newTokenizer`](#newTokenizer): A helper for building simple tokenizers (see also [`nanolightTs`](#nanolightTs)).
71
- - [`omit`](#omit): Creates a new object excluding the specified keys from the source object.
72
- - [`pick`](#pick): Creates a new object containing only the specified keys from the source object.
73
- - [`rwd`](#rwd): A responsive web design helper that generates CSS rules for a grid-like layout.
74
- - [`s`](#s): A lightweight [HyperScript](https://github.com/hyperhype/hyperscript)-style helper for creating and modifying `SVGElement`s (see also [`h`](#h)).
75
- - [`svgUse`](#svgUse): Shorthand for creating an SVG element with a `<use>` child referencing an icon by ID.
76
- - [`uuidV1`](#uuidV1): Generates a UUID v1 (time-based) identifier.
77
- - [`vivify`](#vivify): A Proxy-based helper for auto-vivification of nested object structures.
50
+ - [`CNode`](#CNode):
51
+ Represents a CSS rule node for the [`c`](#c) helper.
52
+ - [`CRoot`](#CRoot):
53
+ Represents the root CSS object for the [`c`](#c) helper.
54
+ - [`HArgs`](#HArgs):
55
+ Tuple argument type for the [`h`](#h) and [`s`](#s)
56
+ helpers.
57
+ - [`HArgs1`](#HArgs1):
58
+ Single argument type for the [`h`](#h) and [`s`](#s)
59
+ helpers.
60
+ - [`c`](#c):
61
+ A minimal CSS-in-JS helper
62
+ that converts a JavaScript object hierarchy
63
+ into a CSS string.
64
+ - [`csvParse`](#csvParse):
65
+ Parses a CSV string
66
+ into a two-dimensional array of strings.
67
+ - [`fixPlTypography`](#fixPlTypography):
68
+ Applies Polish-specific typographic corrections
69
+ to a DOM subtree.
70
+ - [`h`](#h):
71
+ A lightweight *HyperScript*-style helper
72
+ for creating and modifying `HTMLElement`s
73
+ (see also [`s`](#s)).
74
+ - [`hasOwn`](#hasOwn):
75
+ Checks whether an object has the specified key
76
+ as its own property.
77
+ - [`isArray`](#isArray):
78
+ Checks whether the argument is an array.
79
+ - [`isFiniteNumber`](#isFiniteNumber):
80
+ Checks whether the argument is a finite number
81
+ (excludes `±Infinity` and `NaN`).
82
+ - [`isInteger`](#isInteger):
83
+ Checks whether the argument is an integer number.
84
+ - [`isNumber`](#isNumber):
85
+ Checks whether the argument is of type `number`
86
+ (includes `NaN` and `±Infinity`).
87
+ - [`isRecord`](#isRecord):
88
+ Checks whether the argument is a plain object
89
+ (not `null` and not an array).
90
+ - [`isString`](#isString):
91
+ Checks whether the argument is a string.
92
+ - [`jsOnParse`](#jsOnParse):
93
+ Parses JSON with support for
94
+ handler-based value transformation (“JavaScript ON”).
95
+ - [`monokai`](#monokai):
96
+ A Monokai-inspired color scheme
97
+ for use with the [`c`](#c) helper
98
+ and [`nanolightTs`](#nanolightTs) tokenizer.
99
+ - [`nanolightTs`](#nanolightTs):
100
+ A TypeScript/JavaScript syntax highlighting tokenizer
101
+ built using [`newTokenizer`](#newTokenizer).
102
+ - [`newEscape`](#newEscape):
103
+ Creates a tag function
104
+ for escaping interpolated values in template literals.
105
+ - [`newNounForm`](#newNounForm):
106
+ Creates a function that
107
+ returns the appropriate noun form
108
+ based on a numeric value using `Intl.PluralRules`.
109
+ - [`newTokenizer`](#newTokenizer):
110
+ A helper for building simple tokenizers
111
+ (see also [`nanolightTs`](#nanolightTs)).
112
+ - [`omit`](#omit):
113
+ Creates a new object excluding the specified keys
114
+ from the source object.
115
+ - [`pick`](#pick):
116
+ Creates a new object containing only the specified keys
117
+ from the source object.
118
+ - [`rwd`](#rwd):
119
+ A responsive web design helper
120
+ that generates CSS rules for a grid-like layout.
121
+ - [`s`](#s):
122
+ A lightweight *HyperScript*-style helper
123
+ for creating and modifying `SVGElement`s
124
+ (see also [`h`](#h)).
125
+ - [`svgUse`](#svgUse):
126
+ Shorthand for creating an SVG element
127
+ with a `<use>` child referencing an icon by ID.
128
+ - [`uuidV1`](#uuidV1):
129
+ Generates a UUID v1 (time-based) identifier.
130
+ - [`vivify`](#vivify):
131
+ A Proxy-based helper for auto-vivification
132
+ of nested object structures.
78
133
 
79
134
  ### CNode
80
135
 
@@ -84,7 +139,8 @@ type CNode = {
84
139
  };
85
140
  ```
86
141
 
87
- Represents a CSS rule node for the [`c`](#c) helper. Keys are CSS properties or nested selectors.
142
+ Represents a CSS rule node for the [`c`](#c) helper.
143
+ Keys are CSS properties or nested selectors.
88
144
 
89
145
  ### CRoot
90
146
 
@@ -92,7 +148,8 @@ Represents a CSS rule node for the [`c`](#c) helper. Keys are CSS properties or
92
148
  type CRoot = Record<PropertyKey, CNode>;
93
149
  ```
94
150
 
95
- Represents the root CSS object for the [`c`](#c) helper. Keys are top-level selectors or at-rules.
151
+ Represents the root CSS object for the [`c`](#c) helper.
152
+ Keys are top-level selectors or at-rules.
96
153
 
97
154
  ### HArgs
98
155
 
@@ -100,7 +157,8 @@ Represents the root CSS object for the [`c`](#c) helper. Keys are top-level sele
100
157
  type HArgs = [string | Node, ...HArgs1[]];
101
158
  ```
102
159
 
103
- Tuple argument type for the [`h`](#h) and [`s`](#s) helpers.
160
+ Tuple argument type for the [`h`](#h) and [`s`](#s)
161
+ helpers.
104
162
 
105
163
  ### HArgs1
106
164
 
@@ -108,7 +166,8 @@ Tuple argument type for the [`h`](#h) and [`s`](#s) helpers.
108
166
  type HArgs1 = Record<PropertyKey, unknown> | null | undefined | Node | string | number | HArgs;
109
167
  ```
110
168
 
111
- Single argument type for the [`h`](#h) and [`s`](#s) helpers.
169
+ Single argument type for the [`h`](#h) and [`s`](#s)
170
+ helpers.
112
171
 
113
172
  ### c
114
173
 
@@ -116,17 +175,22 @@ Single argument type for the [`h`](#h) and [`s`](#s) helpers.
116
175
  const c: (root: CRoot, splitter?: string) => string;
117
176
  ```
118
177
 
119
- A minimal CSS-in-JS helper that converts a JavaScript object hierarchy into a CSS string.
178
+ A minimal CSS-in-JS helper
179
+ that converts a JavaScript object hierarchy
180
+ into a CSS string.
120
181
 
121
182
  #### root
122
183
 
123
184
  An object describing CSS rules.
124
- Keys are selectors or at-rules; values are either CSS property values or nested rule objects.
185
+ Keys are selectors or at-rules;
186
+ values are either CSS property values
187
+ or nested rule objects.
125
188
 
126
189
  #### splitter
127
190
 
128
191
  A delimiter used to create unique keys (default: `'$$'`).
129
- The substring from `splitter` to the end of a key is ignored (e.g., `src$$1` → `src`).
192
+ The substring from `splitter` to the end of a key
193
+ is ignored (e.g., `src$$1` → `src`).
130
194
 
131
195
  #### Returns
132
196
 
@@ -134,24 +198,33 @@ A CSS string representing the compiled rules.
134
198
 
135
199
  #### Remarks
136
200
 
137
- - Keys whose values are primitives (`string` | `number`) are treated as CSS properties.
138
- - In property keys, uppercase letters become lowercase with a `-` prefix (e.g., `fontFamily` → `font-family`);
139
- underscores become hyphens (e.g., `font_family` → `font-family`).
140
- - Comma-separated selector keys expand into multiple selectors
141
- (e.g., `{ div: { '.a,.b': { margin: 1 } } }` → `div.a,div.b{margin:1}`).
142
- - Top-level keys starting with `@` (at-rules) are not concatenated with child selectors.
201
+ - Keys whose values are primitives (`string` | `number`)
202
+ are treated as CSS properties.
203
+ - In property keys, uppercase letters
204
+ become lowercase with a `-` prefix
205
+ (e.g., `fontFamily` → `font-family`);
206
+ underscores become hyphens
207
+ (e.g., `font_family` → `font-family`).
208
+ - Comma-separated selector keys
209
+ expand into multiple selectors
210
+ (e.g., `{ div: { '.a,.b': { margin: 1 } } }` →
211
+ `div.a,div.b{margin:1}`).
212
+ - Top-level keys starting with `@` (at-rules)
213
+ are not concatenated with child selectors.
143
214
 
144
215
  #### Usage Examples
145
216
 
146
217
  ```ts
147
- const actual1 = c({
148
- a: {
149
- color: 'red',
150
- margin: 1,
151
- '.c': { margin: 2, padding: 2 },
152
- padding: 1,
218
+ const actual1 = c(
219
+ {
220
+ a: {
221
+ color: 'red',
222
+ margin: 1,
223
+ '.c': { margin: 2, padding: 2 },
224
+ padding: 1,
225
+ },
153
226
  },
154
- })
227
+ )
155
228
 
156
229
  const expected1 = `
157
230
  a{
@@ -168,16 +241,18 @@ a{
168
241
 
169
242
  expect(actual1).to.equal(expected1)
170
243
 
171
- const actual2 = c({
172
- a: {
173
- '.b': {
174
- color: 'red',
175
- margin: 1,
176
- '.c': { margin: 2, padding: 2 },
177
- padding: 1,
244
+ const actual2 = c(
245
+ {
246
+ a: {
247
+ '.b': {
248
+ color: 'red',
249
+ margin: 1,
250
+ '.c': { margin: 2, padding: 2 },
251
+ padding: 1,
252
+ },
178
253
  },
179
254
  },
180
- })
255
+ )
181
256
 
182
257
  const expected2 = `
183
258
  a.b{
@@ -194,34 +269,36 @@ a.b{
194
269
 
195
270
  expect(actual2).to.equal(expected2)
196
271
 
197
- const actual3 = c({
198
- '@font-face$$1': {
199
- fontFamily: 'Jackens',
200
- src$$1: 'url(otf/jackens.otf)',
201
- src$$2: "url(otf/jackens.otf) format('opentype')," +
272
+ const actual3 = c(
273
+ {
274
+ '@font-face$$1': {
275
+ fontFamily: 'Jackens',
276
+ src$$1: 'url(otf/jackens.otf)',
277
+ src$$2: "url(otf/jackens.otf) format('opentype')," +
202
278
  "url(svg/jackens.svg) format('svg')",
203
- font_weight: 'normal',
204
- 'font-style': 'normal',
205
- },
206
- '@font-face$$2': {
207
- font_family: 'C64',
208
- src: 'url(fonts/C64_Pro_Mono-STYLE.woff)',
209
- },
210
- '@keyframes spin': {
211
- '0%': { transform: 'rotate(0deg)' },
212
- '100%': { transform: 'rotate(360deg)' },
213
- },
214
- div: {
215
- border: 'solid red 1px',
216
- '.c1': { 'background-color': '#000' },
217
- ' .c1': { background_color: 'black' },
218
- '.c2': { backgroundColor: 'rgb(0,0,0)' },
219
- },
220
- '@media(min-width:200px)': {
221
- div: { margin: 0, padding: 0 },
222
- span: { color: '#000' },
279
+ font_weight: 'normal',
280
+ 'font-style': 'normal',
281
+ },
282
+ '@font-face$$2': {
283
+ font_family: 'C64',
284
+ src: 'url(fonts/C64_Pro_Mono-STYLE.woff)',
285
+ },
286
+ '@keyframes spin': {
287
+ '0%': { transform: 'rotate(0deg)' },
288
+ '100%': { transform: 'rotate(360deg)' },
289
+ },
290
+ div: {
291
+ border: 'solid red 1px',
292
+ '.c1': { 'background-color': '#000' },
293
+ ' .c1': { background_color: 'black' },
294
+ '.c2': { backgroundColor: 'rgb(0,0,0)' },
295
+ },
296
+ '@media(min-width:200px)': {
297
+ div: { margin: 0, padding: 0 },
298
+ span: { color: '#000' },
299
+ },
223
300
  },
224
- })
301
+ )
225
302
 
226
303
  const expected3 = `
227
304
  @font-face{
@@ -267,16 +344,18 @@ div.c2{
267
344
 
268
345
  expect(actual3).to.equal(expected3)
269
346
 
270
- const actual4 = c({
271
- a: {
272
- '.b,.c': {
273
- margin: 1,
274
- '.d': {
275
- margin: 2,
347
+ const actual4 = c(
348
+ {
349
+ a: {
350
+ '.b,.c': {
351
+ margin: 1,
352
+ '.d': {
353
+ margin: 2,
354
+ },
276
355
  },
277
356
  },
278
357
  },
279
- })
358
+ )
280
359
 
281
360
  const expected4 = `
282
361
  a.b,a.c{
@@ -288,14 +367,16 @@ a.b.d,a.c.d{
288
367
 
289
368
  expect(actual4).to.equal(expected4)
290
369
 
291
- const actual5 = c({
292
- '.b,.c': {
293
- margin: 1,
294
- '.d': {
295
- margin: 2,
370
+ const actual5 = c(
371
+ {
372
+ '.b,.c': {
373
+ margin: 1,
374
+ '.d': {
375
+ margin: 2,
376
+ },
296
377
  },
297
378
  },
298
- })
379
+ )
299
380
 
300
381
  const expected5 = `
301
382
  .b,.c{
@@ -307,14 +388,16 @@ const expected5 = `
307
388
 
308
389
  expect(actual5).to.equal(expected5)
309
390
 
310
- const actual6 = c({
311
- '.a,.b': {
312
- margin: 1,
313
- '.c,.d': {
314
- margin: 2,
391
+ const actual6 = c(
392
+ {
393
+ '.a,.b': {
394
+ margin: 1,
395
+ '.c,.d': {
396
+ margin: 2,
397
+ },
315
398
  },
316
399
  },
317
- })
400
+ )
318
401
 
319
402
  const expected6 = `
320
403
  .a,.b{
@@ -333,9 +416,11 @@ expect(actual6).to.equal(expected6)
333
416
  const csvParse: (csv: string, separator?: string) => string[][];
334
417
  ```
335
418
 
336
- Parses a CSV string into a two-dimensional array of strings.
419
+ Parses a CSV string
420
+ into a two-dimensional array of strings.
337
421
 
338
- Supports quoted fields with escaped double quotes (`""`). Carriage returns are normalized.
422
+ Supports quoted fields with escaped double quotes (`""`).
423
+ Carriage returns are normalized.
339
424
 
340
425
  #### csv
341
426
 
@@ -347,7 +432,8 @@ The field delimiter (default: `','`).
347
432
 
348
433
  #### Returns
349
434
 
350
- A 2D array where each inner array represents a row of fields.
435
+ A 2D array where each inner array
436
+ represents a row of fields.
351
437
 
352
438
  #### Usage Examples
353
439
 
@@ -361,11 +447,13 @@ yyy",zzz
361
447
 
362
448
  `
363
449
 
364
- expect(csvParse(text)).to.deep.equal([
365
- ['aaa\n"aaa"\naaa', 'bbb', 'ccc,ccc'],
366
- ['xxx,xxx', 'yyy\nyyy', 'zzz'],
367
- [' 42 ', '42', ' 17'],
368
- ])
450
+ expect(csvParse(text)).to.deep.equal(
451
+ [
452
+ ['aaa\n"aaa"\naaa', 'bbb', 'ccc,ccc'],
453
+ ['xxx,xxx', 'yyy\nyyy', 'zzz'],
454
+ [' 42 ', '42', ' 17'],
455
+ ],
456
+ )
369
457
  ```
370
458
 
371
459
  ### fixPlTypography
@@ -374,22 +462,30 @@ expect(csvParse(text)).to.deep.equal([
374
462
  const fixPlTypography: (node: Node) => void;
375
463
  ```
376
464
 
377
- Applies Polish-specific typographic corrections to a DOM subtree.
465
+ Applies Polish-specific typographic corrections
466
+ to a DOM subtree.
378
467
 
379
- This function prevents orphaned conjunctions (single-letter words like “a”, “i”, “o”, “u”, “w”, “z”)
380
- from appearing at the end of a line by wrapping them with the following word in a non-breaking span.
381
- It also inserts zero-width spaces after slashes and dots to allow line breaks.
468
+ This function prevents orphaned conjunctions
469
+ (single-letter words like a”, “i”, “o”, “u”, “w”, “z”)
470
+ from appearing at the end of a line
471
+ by wrapping them with the following word
472
+ in a non-breaking span.
473
+ It also inserts zero-width spaces
474
+ after slashes and dots to allow line breaks.
382
475
 
383
476
  #### node
384
477
 
385
- The root DOM node to process. All descendant text nodes are corrected recursively,
386
- except those inside `IFRAME`, `NOSCRIPT`, `PRE`, `SCRIPT`, `STYLE`, or `TEXTAREA` elements.
478
+ The root DOM node to process.
479
+ All descendant text nodes are corrected recursively,
480
+ except those inside `IFRAME`, `NOSCRIPT`, `PRE`,
481
+ `SCRIPT`, `STYLE`, or `TEXTAREA` elements.
387
482
 
388
483
  #### Usage Examples
389
484
 
390
485
  ```ts
391
486
  const p = h('p',
392
- 'Pchnąć w tę łódź jeża lub ośm skrzyń fig (zob. https://pl.wikipedia.org/wiki/Pangram).',
487
+ 'Pchnąć w tę łódź jeża lub ośm skrzyń fig ' +
488
+ '(zob. https://pl.wikipedia.org/wiki/Pangram).',
393
489
  ['br'],
394
490
  ['b', 'Zażółć gęślą jaźń.'],
395
491
  )
@@ -397,9 +493,13 @@ const p = h('p',
397
493
  fixPlTypography(p)
398
494
 
399
495
  expect(p.innerHTML).to.deep.equal(
400
- 'Pchnąć <span style="white-space:nowrap">w </span>tę łódź jeża lub ośm skrzyń fig ' +
401
- '(zob. https://\u200Bpl.\u200Bwikipedia.\u200Borg/\u200Bwiki/\u200BPangram).' +
402
- '<br><b>Zażółć gęślą jaźń.</b>')
496
+ 'Pchnąć ' +
497
+ '<span style="white-space:nowrap">w </span>' +
498
+ ' łódź jeża lub ośm skrzyń fig ' +
499
+ '(zob. https://\u200Bpl.\u200Bwikipedia.\u200Borg/' +
500
+ '\u200Bwiki/\u200BPangram).' +
501
+ '<br>' +
502
+ '<b>Zażółć gęślą jaźń.</b>')
403
503
  ```
404
504
 
405
505
  ### h
@@ -412,21 +512,28 @@ const h: {
412
512
  };
413
513
  ```
414
514
 
415
- A lightweight [HyperScript](https://github.com/hyperhype/hyperscript)-style helper for creating and modifying `HTMLElement`s (see also [`s`](#s)).
515
+ A lightweight *HyperScript*-style helper
516
+ for creating and modifying `HTMLElement`s
517
+ (see also [`s`](#s)).
416
518
 
417
519
  #### tagOrNode
418
520
 
419
- If a `string`, it is treated as the tag name for a new element.
521
+ If a `string`, it is treated as the tag name
522
+ for a new element.
420
523
  If a `Node`, that node is modified in place.
421
524
 
422
525
  #### args
423
526
 
424
527
  Additional arguments processed as follows:
425
- - `Object`: maps attributes/properties. Keys starting with `$` set element properties (without the `$` prefix);
426
- other keys set attributes via `setAttribute`. A value of `false` removes the attribute.
528
+ - `Object`: maps attributes/properties.
529
+ Keys starting with `$` set element properties
530
+ (without the `$` prefix);
531
+ other keys set attributes via `setAttribute`.
532
+ A value of `false` removes the attribute.
427
533
  - `null`/`undefined`: ignored.
428
534
  - `Node`: appended as a child.
429
- - `string`/`number`: converted to a `Text` node and appended.
535
+ - `string`/`number`: converted to a `Text` node
536
+ and appended.
430
537
  - [`HArgs`](#HArgs) array: processed recursively.
431
538
 
432
539
  #### Returns
@@ -449,21 +556,32 @@ expect(b.outerHTML).to.equal('<b><i>text</i></b>')
449
556
 
450
557
  h(i, { $className: 'some class' })
451
558
 
452
- expect(i.outerHTML).to.equal('<i class="some class">text</i>')
453
- expect(b.outerHTML).to.equal('<b><i class="some class">text</i></b>')
559
+ expect(i.outerHTML)
560
+ .to.equal('<i class="some class">text</i>')
561
+ expect(b.outerHTML)
562
+ .to.equal('<b><i class="some class">text</i></b>')
454
563
 
455
- expect(h('span', 'text').outerHTML).to.equal('<span>text</span>')
456
- expect(h('span', { $innerText: 'text' }).outerHTML).to.equal('<span>text</span>')
564
+ expect(h('span', 'text').outerHTML)
565
+ .to.equal('<span>text</span>')
566
+ expect(h('span', { $innerText: 'text' }).outerHTML)
567
+ .to.equal('<span>text</span>')
457
568
 
458
- expect(h('span', '42').outerHTML).to.equal('<span>42</span>')
569
+ expect(h('span', '42').outerHTML)
570
+ .to.equal('<span>42</span>')
459
571
  expect(h('span', 42).outerHTML).to.equal('<span>42</span>')
460
572
 
461
573
  expect(h('div', { style: 'margin:0;padding:0' }).outerHTML)
462
574
  .to.equal('<div style="margin:0;padding:0"></div>')
463
575
  expect(h('div', { $style: 'margin:0;padding:0' }).outerHTML)
464
- .to.equal('<div style="margin: 0px; padding: 0px;"></div>')
465
- expect(h('div', { $style: { margin: 0, padding: 0 } }).outerHTML)
466
- .to.equal('<div style="margin: 0px; padding: 0px;"></div>')
576
+ .to.equal(
577
+ '<div style="margin: 0px; padding: 0px;"></div>',
578
+ )
579
+ expect(
580
+ h(
581
+ 'div',
582
+ { $style: { margin: 0, padding: 0 } },
583
+ ).outerHTML,
584
+ ).to.equal('<div style="margin: 0px; padding: 0px;"></div>')
467
585
 
468
586
  const input1 = h('input', { value: 42 })
469
587
  const input2 = h('input', { $value: '42' })
@@ -474,14 +592,22 @@ expect(input2.value).to.equal('42')
474
592
  expect(input1.outerHTML).to.equal('<input value="42">')
475
593
  expect(input2.outerHTML).to.equal('<input>')
476
594
 
477
- const checkbox1 = h('input', { type: 'checkbox', checked: true })
478
- const checkbox2 = h('input', { type: 'checkbox', $checked: true })
595
+ const checkbox1 = h(
596
+ 'input',
597
+ { type: 'checkbox', checked: true },
598
+ )
599
+ const checkbox2 = h(
600
+ 'input',
601
+ { type: 'checkbox', $checked: true },
602
+ )
479
603
 
480
604
  expect(checkbox1.checked).to.be.true
481
605
  expect(checkbox2.checked).to.be.true
482
606
 
483
- expect(checkbox1.outerHTML).to.equal('<input type="checkbox" checked="">')
484
- expect(checkbox2.outerHTML).to.equal('<input type="checkbox">')
607
+ expect(checkbox1.outerHTML)
608
+ .to.equal('<input type="checkbox" checked="">')
609
+ expect(checkbox2.outerHTML)
610
+ .to.equal('<input type="checkbox">')
485
611
 
486
612
  const div = h('div')
487
613
 
@@ -502,11 +628,15 @@ expect(elemWithClass.getAttribute('class')).to.equal('test')
502
628
  const elemWithText = h('div', 'initial')
503
629
 
504
630
  h(elemWithText, ' more')
505
- expect(elemWithText.outerHTML).to.equal('<div>initial more</div>')
631
+ expect(elemWithText.outerHTML)
632
+ .to.equal('<div>initial more</div>')
506
633
 
507
- const elemWithNested = h('div', ['span', 'hello'], ['b', 'world'])
634
+ const elemWithNested = h('div',
635
+ ['span', 'hello'],
636
+ ['b', 'world'])
508
637
 
509
- expect(elemWithNested.outerHTML).to.equal('<div><span>hello</span><b>world</b></div>')
638
+ expect(elemWithNested.outerHTML)
639
+ .to.equal('<div><span>hello</span><b>world</b></div>')
510
640
  ```
511
641
 
512
642
  ### hasOwn
@@ -515,7 +645,8 @@ expect(elemWithNested.outerHTML).to.equal('<div><span>hello</span><b>world</b></
515
645
  const hasOwn: (ref: unknown, key: unknown) => boolean;
516
646
  ```
517
647
 
518
- Checks whether an object has the specified key as its own property.
648
+ Checks whether an object has the specified key
649
+ as its own property.
519
650
 
520
651
  A null-safe wrapper around `Object.hasOwn`.
521
652
 
@@ -529,7 +660,8 @@ The property key to look for.
529
660
 
530
661
  #### Returns
531
662
 
532
- `true` if `ref` is not nullish and has `key` as an own property, `false` otherwise.
663
+ `true` if `ref` is not nullish
664
+ and has `key` as an own property, `false` otherwise.
533
665
 
534
666
  #### Usage Examples
535
667
 
@@ -584,8 +716,15 @@ The value to check.
584
716
 
585
717
  ```ts
586
718
  expect(isArray([])).to.be.true
587
- expect(isArray(Object.create({ constructor: Array }))).to.be.false
588
- expect(isArray(Object.create({ [Symbol.toStringTag]: Array.name }))).to.be.false
719
+
720
+ const fakeArray1 = Object.create({ constructor: Array })
721
+
722
+ expect(isArray(fakeArray1)).to.be.false
723
+
724
+ const fakeArray2 =
725
+ Object.create({ [Symbol.toStringTag]: Array.name })
726
+
727
+ expect(isArray(fakeArray2)).to.be.false
589
728
  ```
590
729
 
591
730
  ### isFiniteNumber
@@ -594,7 +733,8 @@ expect(isArray(Object.create({ [Symbol.toStringTag]: Array.name }))).to.be.false
594
733
  const isFiniteNumber: (arg: unknown) => arg is number;
595
734
  ```
596
735
 
597
- Checks whether the argument is a finite number (excludes `±Infinity` and `NaN`).
736
+ Checks whether the argument is a finite number
737
+ (excludes `±Infinity` and `NaN`).
598
738
 
599
739
  #### arg
600
740
 
@@ -635,7 +775,8 @@ The value to check.
635
775
  ```ts
636
776
  expect(isInteger(42)).to.be.true
637
777
  expect(isInteger(42.00000000000001)).to.be.false
638
- expect(isInteger(42.000000000000001)).to.be.true // because of loss of precision
778
+ expect(isInteger(42.000000000000001)).to.be.true
779
+ // ^because of loss of precision
639
780
  expect(isInteger(Number(42))).to.be.true
640
781
  expect(isInteger(new Number(42))).to.be.false
641
782
  expect(isInteger(NaN)).to.be.false
@@ -648,7 +789,8 @@ expect(isInteger(Infinity)).to.be.false
648
789
  const isNumber: (arg: unknown) => arg is number;
649
790
  ```
650
791
 
651
- Checks whether the argument is of type `number` (includes `NaN` and `±Infinity`).
792
+ Checks whether the argument is of type `number`
793
+ (includes `NaN` and `±Infinity`).
652
794
 
653
795
  #### arg
654
796
 
@@ -674,7 +816,8 @@ expect(isNumber(Infinity)).to.be.true
674
816
  const isRecord: (arg: unknown) => arg is Record<PropertyKey, unknown>;
675
817
  ```
676
818
 
677
- Checks whether the argument is a plain object (not `null` and not an array).
819
+ Checks whether the argument is a plain object
820
+ (not `null` and not an array).
678
821
 
679
822
  #### arg
680
823
 
@@ -690,8 +833,16 @@ The value to check.
690
833
  expect(isRecord({})).to.be.true
691
834
  expect(isRecord([])).to.be.false
692
835
  expect(isRecord(Object.create(null))).to.be.true
693
- expect(isRecord(Object.create({ constructor: Number }))).to.be.true
694
- expect(isRecord(Object.create({ [Symbol.toStringTag]: Number.name }))).to.be.true
836
+
837
+ const fakeNumber1 = Object.create({ constructor: Number })
838
+
839
+ expect(isRecord(fakeNumber1)).to.be.true
840
+
841
+ const fakeNumber2 =
842
+ Object.create({ [Symbol.toStringTag]: Number.name })
843
+
844
+ expect(isRecord(fakeNumber2)).to.be.true
845
+
695
846
  expect(isRecord(new Number(42))).to.be.true
696
847
  expect(isRecord(new String('42'))).to.be.true
697
848
 
@@ -730,10 +881,14 @@ expect(isString(new String('42'))).to.be.false
730
881
  const jsOnParse: (handlers: Record<PropertyKey, Function>, text: string) => any;
731
882
  ```
732
883
 
733
- Parses JSON with support for handler-based value transformation (“JavaScript ON”).
884
+ Parses JSON with support for
885
+ handler-based value transformation (“JavaScript ON”).
734
886
 
735
- Objects with exactly one property whose key exists in `handlers` and whose value is an array
736
- are replaced by invoking the corresponding handler with the array elements as arguments.
887
+ Objects with exactly one property
888
+ whose key exists in `handlers`
889
+ and whose value is an array
890
+ are replaced by invoking the corresponding handler
891
+ with the array elements as arguments.
737
892
 
738
893
  #### handlers
739
894
 
@@ -780,33 +935,49 @@ const actual1 = jsOnParse(handlers, `[
780
935
  }
781
936
  ]`)
782
937
 
783
- expect(actual1).to.deep.equal([
784
- 3,
785
- 'Hello World!',
786
- {
787
- nested: 'Hello nested World!',
788
- one: 1,
789
- two: 2,
790
- },
791
- 'bar',
792
- {
793
- $foo: ['The parent object does not have exactly one property!'],
794
- one: 1,
795
- two: 2,
796
- },
797
- ])
938
+ expect(actual1).to.deep.equal(
939
+ [
940
+ 3,
941
+ 'Hello World!',
942
+ {
943
+ nested: 'Hello nested World!',
944
+ one: 1,
945
+ two: 2,
946
+ },
947
+ 'bar',
948
+ {
949
+ $foo: [
950
+ 'The parent object does not have ' +
951
+ 'exactly one property!',
952
+ ],
953
+ one: 1,
954
+ two: 2,
955
+ },
956
+ ],
957
+ )
798
958
 
799
- const actual2 = jsOnParse({ $notFunc: 'handler not being a function' } as any, '{"$notFunc": [1, 2]}')
959
+ const actual2 = jsOnParse(
960
+ { $notFunc: 'handler not being a function' } as any,
961
+ '{"$notFunc": [1, 2]}',
962
+ )
800
963
 
801
964
  expect(actual2).to.deep.equal({ $notFunc: [1, 2] })
802
965
 
803
- const actual3 = jsOnParse(handlers, '{"$unknown_handler_key": [1, 2]}')
966
+ const actual3 = jsOnParse(
967
+ handlers,
968
+ '{"$unknown_handler_key": [1, 2]}',
969
+ )
804
970
 
805
- expect(actual3).to.deep.equal({ $unknown_handler_key: [1, 2] })
971
+ expect(actual3)
972
+ .to.deep.equal({ $unknown_handler_key: [1, 2] })
806
973
 
807
- const actual4 = jsOnParse(handlers, '{"$add": {"not": "array"}}')
974
+ const actual4 = jsOnParse(
975
+ handlers,
976
+ '{"$add": {"not": "array"}}',
977
+ )
808
978
 
809
- expect(actual4).to.deep.equal({ $add: { not: 'array' } })
979
+ expect(actual4)
980
+ .to.deep.equal({ $add: { not: 'array' } })
810
981
  ```
811
982
 
812
983
  ### monokai
@@ -815,7 +986,9 @@ expect(actual4).to.deep.equal({ $add: { not: 'array' } })
815
986
  const monokai: CRoot;
816
987
  ```
817
988
 
818
- A Monokai-inspired color scheme for use with the [`c`](#c) helper and [`nanolightTs`](#nanolightTs) tokenizer.
989
+ A Monokai-inspired color scheme
990
+ for use with the [`c`](#c) helper
991
+ and [`nanolightTs`](#nanolightTs) tokenizer.
819
992
 
820
993
  ### nanolightTs
821
994
 
@@ -823,7 +996,8 @@ A Monokai-inspired color scheme for use with the [`c`](#c) helper and [`nanoligh
823
996
  const nanolightTs: (code: string) => HArgs1[];
824
997
  ```
825
998
 
826
- A TypeScript/JavaScript syntax highlighting tokenizer built using [`newTokenizer`](#newTokenizer).
999
+ A TypeScript/JavaScript syntax highlighting tokenizer
1000
+ built using [`newTokenizer`](#newTokenizer).
827
1001
 
828
1002
  #### code
829
1003
 
@@ -831,34 +1005,42 @@ The source code string to tokenize.
831
1005
 
832
1006
  #### Returns
833
1007
 
834
- An array of [`HArgs1`](#HArgs1) elements suitable for rendering with [`h`](#h).
1008
+ An array of [`HArgs1`](#HArgs1) elements
1009
+ suitable for rendering with [`h`](#h).
835
1010
 
836
1011
  #### Usage Examples
837
1012
 
838
1013
  ```ts
839
- const codeJs = "const answerToLifeTheUniverseAndEverything = { 42: 42 }['42'] /* 42 */"
840
-
841
- expect(nanolightTs(codeJs)).to.deep.equal([
842
- ['span', { class: 'keyword-1' }, 'const'],
843
- ' ',
844
- ['span', { class: 'identifier-4' }, 'answerToLifeTheUniverseAndEverything'],
845
- ' ',
846
- ['span', { class: 'operator' }, '='],
847
- ' ',
848
- ['span', { class: 'punctuation' }, '{'],
849
- ' ',
850
- ['span', { class: 'number' }, '42'],
851
- ['span', { class: 'operator' }, ':'],
852
- ' ',
853
- ['span', { class: 'number' }, '42'],
854
- ' ',
855
- ['span', { class: 'punctuation' }, '}'],
856
- ['span', { class: 'punctuation' }, '['],
857
- ['span', { class: 'string' }, "'42'"],
858
- ['span', { class: 'punctuation' }, ']'],
859
- ' ',
860
- ['span', { class: 'comment' }, '/* 42 */'],
861
- ])
1014
+ const codeJs =
1015
+ 'const answerToLifeTheUniverseAndEverything = ' +
1016
+ "{ 42: 42 }['42'] /* 42 */"
1017
+
1018
+ expect(nanolightTs(codeJs)).to.deep.equal(
1019
+ [
1020
+ ['span', { class: 'keyword-1' }, 'const'],
1021
+ ' ',
1022
+ ['span',
1023
+ { class: 'identifier-4' },
1024
+ 'answerToLifeTheUniverseAndEverything',
1025
+ ],
1026
+ ' ',
1027
+ ['span', { class: 'operator' }, '='],
1028
+ ' ',
1029
+ ['span', { class: 'punctuation' }, '{'],
1030
+ ' ',
1031
+ ['span', { class: 'number' }, '42'],
1032
+ ['span', { class: 'operator' }, ':'],
1033
+ ' ',
1034
+ ['span', { class: 'number' }, '42'],
1035
+ ' ',
1036
+ ['span', { class: 'punctuation' }, '}'],
1037
+ ['span', { class: 'punctuation' }, '['],
1038
+ ['span', { class: 'string' }, "'42'"],
1039
+ ['span', { class: 'punctuation' }, ']'],
1040
+ ' ',
1041
+ ['span', { class: 'comment' }, '/* 42 */'],
1042
+ ],
1043
+ )
862
1044
  ```
863
1045
 
864
1046
  ### newEscape
@@ -867,15 +1049,18 @@ expect(nanolightTs(codeJs)).to.deep.equal([
867
1049
  const newEscape: (escapeFn: (value: any) => string) => (template: TemplateStringsArray, ...values: unknown[]) => string;
868
1050
  ```
869
1051
 
870
- Creates a tag function for escaping interpolated values in template literals.
1052
+ Creates a tag function
1053
+ for escaping interpolated values in template literals.
871
1054
 
872
1055
  #### escapeFn
873
1056
 
874
- A function that takes a value and returns its escaped string representation.
1057
+ A function that takes a value
1058
+ and returns its escaped string representation.
875
1059
 
876
1060
  #### Returns
877
1061
 
878
- A tag function that escapes interpolated values using the provided escape map.
1062
+ A tag function that escapes interpolated values
1063
+ using the provided escape map.
879
1064
 
880
1065
  #### Usage Examples
881
1066
 
@@ -898,12 +1083,27 @@ const sql = newEscape(escapeFn)
898
1083
  const actual = sql`
899
1084
  SELECT *
900
1085
  FROM table_name
901
- WHERE column_name IN (${[true, null, undefined, NaN, Infinity, 42, '42', "4'2", /42/, new Date(323325000000)]})`
1086
+ WHERE column_name IN (
1087
+ ${[
1088
+ true,
1089
+ null,
1090
+ undefined,
1091
+ NaN,
1092
+ Infinity,
1093
+ 42,
1094
+ '42',
1095
+ "4'2",
1096
+ /42/,
1097
+ new Date(323325000000),
1098
+ ]}
1099
+ )`
902
1100
 
903
1101
  const expected = `
904
1102
  SELECT *
905
1103
  FROM table_name
906
- WHERE column_name IN (b'1', NULL, NULL, NULL, NULL, 42, '42', '4''2', NULL, '1980-03-31 04:30:00')`
1104
+ WHERE column_name IN (
1105
+ b'1', NULL, NULL, NULL, NULL, 42, '42', '4''2', NULL, '1980-03-31 04:30:00'
1106
+ )`
907
1107
 
908
1108
  expect(actual).to.equal(expected)
909
1109
  ```
@@ -914,14 +1114,20 @@ expect(actual).to.equal(expected)
914
1114
  const newNounForm: (locale: string, forms: Partial<Record<Intl.LDMLPluralRule, string>>) => (value: number) => string;
915
1115
  ```
916
1116
 
917
- Creates a function that returns the appropriate noun form based on a numeric value using `Intl.PluralRules`.
1117
+ Creates a function that
1118
+ returns the appropriate noun form
1119
+ based on a numeric value using `Intl.PluralRules`.
918
1120
 
919
- Different languages have different plural rules. The `Intl.PluralRules` API provides locale-aware plural category selection.
1121
+ Different languages have different plural rules.
1122
+ The `Intl.PluralRules` API provides locale-aware
1123
+ plural category selection.
920
1124
  Possible categories are:
921
1125
 
922
- - `zero`: for zero items (used in some languages like Arabic, Latvian)
1126
+ - `zero`: for zero items
1127
+ (used in some languages like Arabic, Latvian)
923
1128
  - `one`: for singular (e.g., 1 item)
924
- - `two`: for dual (used in some languages like Arabic, Hebrew)
1129
+ - `two`: for dual
1130
+ (used in some languages like Arabic, Hebrew)
925
1131
  - `few`: for small plurals (e.g., 2-4 in Polish)
926
1132
  - `many`: for larger plurals (e.g., 5-21 in Polish)
927
1133
  - `other`: fallback category (used by all languages)
@@ -932,17 +1138,24 @@ A BCP 47 language tag (e.g., `pl`, `en`).
932
1138
 
933
1139
  #### forms
934
1140
 
935
- An object mapping plural categories to noun forms. Not all categories need to be provided;
936
- if a category is missing, the function falls back to `other`, then to an empty string.
1141
+ An object mapping plural categories to noun forms.
1142
+ Not all categories need to be provided;
1143
+ if a category is missing,
1144
+ the function falls back to `other`,
1145
+ then to an empty string.
937
1146
 
938
1147
  #### Returns
939
1148
 
940
- A function that takes a numeric value and returns the appropriate noun form.
1149
+ A function that takes a numeric value
1150
+ and returns the appropriate noun form.
941
1151
 
942
1152
  #### Usage Examples
943
1153
 
944
1154
  ```ts
945
- const auto = newNounForm('pl', { one: 'auto', few: 'auta', other: 'aut' })
1155
+ const auto = newNounForm(
1156
+ 'pl',
1157
+ { one: 'auto', few: 'auta', other: 'aut' },
1158
+ )
946
1159
 
947
1160
  expect(auto(0)).to.equal('aut')
948
1161
  expect(auto(1)).to.equal('auto')
@@ -970,29 +1183,39 @@ expect(empty(42)).to.equal('')
970
1183
  const newTokenizer: <M, T>(decorator: (chunk: string, metadata?: M) => T, ...specs: [M, string | RegExp][]) => (code: string) => T[];
971
1184
  ```
972
1185
 
973
- A helper for building simple tokenizers (see also [`nanolightTs`](#nanolightTs)).
1186
+ A helper for building simple tokenizers
1187
+ (see also [`nanolightTs`](#nanolightTs)).
974
1188
 
975
1189
  #### decorator
976
1190
 
977
- A function that wraps each matched chunk. It receives the matched text (`chunk`)
978
- and optionally the `metadata` associated with the pattern that produced the match.
979
- For unmatched text between patterns, `metadata` is `undefined`.
1191
+ A function that wraps each matched chunk.
1192
+ It receives the matched text (`chunk`)
1193
+ and optionally the `metadata`
1194
+ associated with the pattern that produced the match.
1195
+ For unmatched text between patterns,
1196
+ `metadata` is `undefined`.
980
1197
 
981
1198
  #### specs
982
1199
 
983
1200
  An array of tuples `[metadata, pattern]` where:
984
- - `metadata`: arbitrary data (e.g., a CSS class name) passed to `decorator` when the pattern matches.
985
- - `pattern`: a `string` or `RegExp` to match against the input.
1201
+ - `metadata`: arbitrary data (e.g., a CSS class name)
1202
+ passed to `decorator` when the pattern matches.
1203
+ - `pattern`: a `string` or `RegExp`
1204
+ to match against the input.
986
1205
 
987
1206
  #### Returns
988
1207
 
989
- A tokenizer function that accepts a code string and returns an array of decorated tokens.
1208
+ A tokenizer function that accepts a code string
1209
+ and returns an array of decorated tokens.
990
1210
 
991
1211
  #### Remarks
992
1212
 
993
- 1. Matches starting at an earlier position take precedence.
994
- 2. Among matches at the same position, the longer one wins.
995
- 3. Among matches of the same position and length, the one defined earlier wins.
1213
+ 1. Matches starting at an earlier position
1214
+ take precedence.
1215
+ 2. Among matches at the same position,
1216
+ the longer one wins.
1217
+ 3. Among matches of the same position and length,
1218
+ the one defined earlier wins.
996
1219
 
997
1220
  #### Usage Examples
998
1221
 
@@ -1004,15 +1227,17 @@ const tokenizer1 = newTokenizer(
1004
1227
  )
1005
1228
  const result1 = tokenizer1('if "hello" else "world"')
1006
1229
 
1007
- expect(result1).to.deep.equal([
1008
- { chunk: 'if', metadata: 'keyword' },
1009
- { chunk: ' ', metadata: undefined },
1010
- { chunk: '"hello"', metadata: 'string' },
1011
- { chunk: ' ', metadata: undefined },
1012
- { chunk: 'else', metadata: 'keyword' },
1013
- { chunk: ' ', metadata: undefined },
1014
- { chunk: '"world"', metadata: 'string' },
1015
- ])
1230
+ expect(result1).to.deep.equal(
1231
+ [
1232
+ { chunk: 'if', metadata: 'keyword' },
1233
+ { chunk: ' ', metadata: undefined },
1234
+ { chunk: '"hello"', metadata: 'string' },
1235
+ { chunk: ' ', metadata: undefined },
1236
+ { chunk: 'else', metadata: 'keyword' },
1237
+ { chunk: ' ', metadata: undefined },
1238
+ { chunk: '"world"', metadata: 'string' },
1239
+ ],
1240
+ )
1016
1241
 
1017
1242
  const tokenizer2 = newTokenizer(
1018
1243
  (chunk, metadata) => `${metadata}:${chunk}`,
@@ -1021,7 +1246,15 @@ const tokenizer2 = newTokenizer(
1021
1246
  )
1022
1247
  const result2 = tokenizer2('aBEGINbENDc')
1023
1248
 
1024
- expect(result2).to.deep.equal(['undefined:a', 'tag:BEGIN', 'undefined:b', 'end:END', 'undefined:c'])
1249
+ expect(result2).to.deep.equal(
1250
+ [
1251
+ 'undefined:a',
1252
+ 'tag:BEGIN',
1253
+ 'undefined:b',
1254
+ 'end:END',
1255
+ 'undefined:c',
1256
+ ],
1257
+ )
1025
1258
 
1026
1259
  const tokenizer3 = newTokenizer(
1027
1260
  (chunk) => chunk,
@@ -1037,10 +1270,12 @@ const tokenizer4 = newTokenizer(
1037
1270
  )
1038
1271
  const result4 = tokenizer4('test here')
1039
1272
 
1040
- expect(result4).to.deep.equal([
1041
- { chunk: 'test', metadata: 'start' },
1042
- { chunk: ' here', metadata: undefined },
1043
- ])
1273
+ expect(result4).to.deep.equal(
1274
+ [
1275
+ { chunk: 'test', metadata: 'start' },
1276
+ { chunk: ' here', metadata: undefined },
1277
+ ],
1278
+ )
1044
1279
 
1045
1280
  const tokenizer5 = newTokenizer(
1046
1281
  (chunk, metadata) => metadata,
@@ -1078,7 +1313,7 @@ const result8 = tokenizer8('abc')
1078
1313
  expect(result8).to.deep.equal(['abc'])
1079
1314
 
1080
1315
  const tokenizer9 = newTokenizer(
1081
- (chunk, metadata) => metadata,
1316
+ (_chunk, metadata) => metadata,
1082
1317
  ['a', 'a'],
1083
1318
  ['b', 'b'],
1084
1319
  )
@@ -1093,9 +1328,12 @@ expect(result9).to.deep.equal(['a', 'a', 'b', 'b'])
1093
1328
  const omit: <T, K extends keyof T>(ref: T, keys: unknown[]) => Omit<T, K>;
1094
1329
  ```
1095
1330
 
1096
- Creates a new object excluding the specified keys from the source object.
1331
+ Creates a new object excluding the specified keys
1332
+ from the source object.
1097
1333
 
1098
- A runtime equivalent of TypeScript’s `Omit<T, K>` utility type. See also [`pick`](#pick).
1334
+ A runtime equivalent
1335
+ of TypeScript’s `Omit<T, K>` utility type.
1336
+ See also [`pick`](#pick).
1099
1337
 
1100
1338
  #### ref
1101
1339
 
@@ -1120,12 +1358,15 @@ expect(omit(ref, ['c'])).to.deep.equal({ a: 42, b: '42' })
1120
1358
  ### pick
1121
1359
 
1122
1360
  ```ts
1123
- const pick: <T, K extends keyof T>(ref: T, keys: K[]) => Pick<T, K>;
1361
+ const pick: <T, K extends keyof T>(ref: T, keys: unknown[]) => Pick<T, K>;
1124
1362
  ```
1125
1363
 
1126
- Creates a new object containing only the specified keys from the source object.
1364
+ Creates a new object containing only the specified keys
1365
+ from the source object.
1127
1366
 
1128
- A runtime equivalent of TypeScript’s `Pick<T, K>` utility type. See also [`omit`](#omit).
1367
+ A runtime equivalent
1368
+ of TypeScript’s `Pick<T, K>` utility type.
1369
+ See also [`omit`](#omit).
1129
1370
 
1130
1371
  #### ref
1131
1372
 
@@ -1144,7 +1385,9 @@ A new object with only the specified keys.
1144
1385
  ```ts
1145
1386
  const ref = { a: 42, b: '42', c: 17 }
1146
1387
 
1147
- expect(pick(ref, ['a', 'b'])).to.deep.equal({ a: 42, b: '42' })
1388
+ expect(pick(ref, ['a', 'b'])).to.deep.equal(
1389
+ { a: 42, b: '42' },
1390
+ )
1148
1391
  ```
1149
1392
 
1150
1393
  ### rwd
@@ -1153,7 +1396,8 @@ expect(pick(ref, ['a', 'b'])).to.deep.equal({ a: 42, b: '42' })
1153
1396
  const rwd: (root: CRoot, selector: string, cellWidthPx: number, cellHeightPx: number, ...specs: [number, number?, number?][]) => void;
1154
1397
  ```
1155
1398
 
1156
- A responsive web design helper that generates CSS rules for a grid-like layout.
1399
+ A responsive web design helper
1400
+ that generates CSS rules for a grid-like layout.
1157
1401
 
1158
1402
  #### root
1159
1403
 
@@ -1174,9 +1418,12 @@ The base cell height in pixels.
1174
1418
  #### specs
1175
1419
 
1176
1420
  An array of breakpoint specifications, each a tuple of:
1177
- - `maxWidth`: maximum number of cells per row (defines the viewport breakpoint).
1178
- - `width` (optional, default `1`): number of horizontal cells the element spans.
1179
- - `height` (optional, default `1`): number of vertical cells the element spans.
1421
+ - `maxWidth`: maximum number of cells per row
1422
+ (defines the viewport breakpoint).
1423
+ - `width` (optional, default `1`):
1424
+ number of horizontal cells the element spans.
1425
+ - `height` (optional, default `1`):
1426
+ number of vertical cells the element spans.
1180
1427
 
1181
1428
  #### Usage Examples
1182
1429
 
@@ -1195,34 +1442,36 @@ const style: CRoot = {
1195
1442
 
1196
1443
  rwd(style, '.r6', 200, 50, [6], [3], [1, 1, 2])
1197
1444
 
1198
- expect(style).to.deep.equal({
1199
- body: {
1200
- margin: 0,
1201
- },
1202
- '.r6': {
1203
- border: 'solid red 1px',
1204
- '.no-border': {
1205
- border: 'none',
1445
+ expect(style).to.deep.equal(
1446
+ {
1447
+ body: {
1448
+ margin: 0,
1206
1449
  },
1207
- boxSizing: 'border-box',
1208
- display: 'block',
1209
- float: 'left',
1210
- width: '100%',
1211
- height: '100px',
1212
- },
1213
- '@media(min-width:600px)': {
1214
1450
  '.r6': {
1215
- width: 'calc(100% / 3)',
1216
- height: '50px',
1451
+ border: 'solid red 1px',
1452
+ '.no-border': {
1453
+ border: 'none',
1454
+ },
1455
+ boxSizing: 'border-box',
1456
+ display: 'block',
1457
+ float: 'left',
1458
+ width: '100%',
1459
+ height: '100px',
1217
1460
  },
1218
- },
1219
- '@media(min-width:1200px)': {
1220
- '.r6': {
1221
- width: 'calc(50% / 3)',
1222
- height: '50px',
1461
+ '@media(min-width:600px)': {
1462
+ '.r6': {
1463
+ width: 'calc(100% / 3)',
1464
+ height: '50px',
1465
+ },
1466
+ },
1467
+ '@media(min-width:1200px)': {
1468
+ '.r6': {
1469
+ width: 'calc(50% / 3)',
1470
+ height: '50px',
1471
+ },
1223
1472
  },
1224
1473
  },
1225
- })
1474
+ )
1226
1475
  ```
1227
1476
 
1228
1477
  ### s
@@ -1235,21 +1484,28 @@ const s: {
1235
1484
  };
1236
1485
  ```
1237
1486
 
1238
- A lightweight [HyperScript](https://github.com/hyperhype/hyperscript)-style helper for creating and modifying `SVGElement`s (see also [`h`](#h)).
1487
+ A lightweight *HyperScript*-style helper
1488
+ for creating and modifying `SVGElement`s
1489
+ (see also [`h`](#h)).
1239
1490
 
1240
1491
  #### tagOrNode
1241
1492
 
1242
- If a `string`, it is treated as the tag name for a new element.
1493
+ If a `string`, it is treated as the tag name
1494
+ for a new element.
1243
1495
  If a `Node`, that node is modified in place.
1244
1496
 
1245
1497
  #### args
1246
1498
 
1247
1499
  Additional arguments processed as follows:
1248
- - `Object`: maps attributes/properties. Keys starting with `$` set element properties (without the `$` prefix);
1249
- other keys set attributes via `setAttributeNS`. A value of `false` removes the attribute.
1500
+ - `Object`: maps attributes/properties.
1501
+ Keys starting with `$` set element properties
1502
+ (without the `$` prefix);
1503
+ other keys set attributes via `setAttributeNS`.
1504
+ A value of `false` removes the attribute.
1250
1505
  - `null`/`undefined`: ignored.
1251
1506
  - `Node`: appended as a child.
1252
- - `string`/`number`: converted to a `Text` node and appended.
1507
+ - `string`/`number`: converted to a `Text` node
1508
+ and appended.
1253
1509
  - [`HArgs`](#HArgs) array: processed recursively.
1254
1510
 
1255
1511
  #### Returns
@@ -1259,25 +1515,28 @@ The created or modified `SVGElement`.
1259
1515
  #### Usage Examples
1260
1516
 
1261
1517
  ```ts
1262
- const svg = s('svg')
1518
+ const XLINK_NS = 'http://www.w3.org/1999/xlink'
1519
+
1520
+ const svg1 = s('svg', { 'xlink:href': true })
1263
1521
 
1264
- s(svg, { 'xlink:href': true })
1265
- expect(svg.getAttributeNS('http://www.w3.org/1999/xlink', 'href')).to.equal('')
1522
+ expect(svg1.getAttributeNS(XLINK_NS, 'href')).to.equal('')
1266
1523
 
1267
- const svg2 = s('svg')
1524
+ const svg2 = s('svg', { 'xlink:href': false })
1268
1525
 
1269
- s(svg2, { 'xlink:href': false })
1270
- expect(svg2.getAttributeNS('http://www.w3.org/1999/xlink', 'href')).to.be.null
1526
+ expect(svg2.getAttributeNS(XLINK_NS, 'href')).to.be.null
1271
1527
 
1272
- const svg3 = s('svg')
1528
+ const svg3 = s(
1529
+ 'svg',
1530
+ { 'xlink:href': 'http://example.com' },
1531
+ )
1273
1532
 
1274
- s(svg3, { 'xlink:href': 'http://example.com' })
1275
- expect(svg3.getAttributeNS('http://www.w3.org/1999/xlink', 'href')).to.equal('http://example.com')
1533
+ expect(svg3.getAttributeNS(XLINK_NS, 'href'))
1534
+ .to.equal('http://example.com')
1276
1535
 
1277
- const svg4 = s('svg')
1536
+ const svg4 = s('svg', { 'xlink:title': 42 })
1278
1537
 
1279
- s(svg4, { 'xlink:title': 42 })
1280
- expect(svg4.getAttributeNS('http://www.w3.org/1999/xlink', 'title')).to.equal('42')
1538
+ expect(svg4.getAttributeNS(XLINK_NS, 'title'))
1539
+ .to.equal('42')
1281
1540
  ```
1282
1541
 
1283
1542
  ### svgUse
@@ -1286,13 +1545,16 @@ expect(svg4.getAttributeNS('http://www.w3.org/1999/xlink', 'title')).to.equal('4
1286
1545
  const svgUse: (id: string, ...args: HArgs1[]) => SVGSVGElement;
1287
1546
  ```
1288
1547
 
1289
- Shorthand for creating an SVG element with a `<use>` child referencing an icon by ID.
1548
+ Shorthand for creating an SVG element
1549
+ with a `<use>` child referencing an icon by ID.
1290
1550
 
1291
- Equivalent to: `s('svg', ['use', { 'xlink:href': '#' + id }], ...args)`.
1551
+ Equivalent to:
1552
+ `s('svg', ['use', { 'xlink:href': '#' + id }], ...args)`.
1292
1553
 
1293
1554
  #### id
1294
1555
 
1295
- The ID of the symbol to reference (without the `#` prefix).
1556
+ The ID of the symbol to reference
1557
+ (without the `#` prefix).
1296
1558
 
1297
1559
  #### args
1298
1560
 
@@ -1309,40 +1571,55 @@ const XLINK_NS = 'http://www.w3.org/1999/xlink'
1309
1571
 
1310
1572
  const svg = svgUse('icon-home')
1311
1573
 
1312
- expect(svg.tagName).to.equal('SVG')
1313
1574
  expect(svg.children.length).to.equal(1)
1314
1575
 
1315
1576
  const useElement = svg.children[0]
1316
1577
 
1317
- expect(useElement.tagName).to.equal('USE')
1318
- expect(useElement.getAttributeNS(XLINK_NS, 'href')).to.equal('#icon-home')
1578
+ expect(useElement.getAttributeNS(XLINK_NS, 'href'))
1579
+ .to.equal('#icon-home')
1319
1580
 
1320
- const svgWithViewBox = svgUse('icon-star', { viewBox: '0 0 24 24' })
1581
+ const svgWithViewBox =
1582
+ svgUse('icon-star', { viewBox: '0 0 24 24' })
1321
1583
 
1322
- expect(svgWithViewBox.getAttribute('viewBox')).to.equal('0 0 24 24')
1584
+ expect(svgWithViewBox.getAttribute('viewBox'))
1585
+ .to.equal('0 0 24 24')
1323
1586
 
1324
1587
  const useViewBox = svgWithViewBox.children[0]
1325
1588
 
1326
- expect(useViewBox.getAttributeNS(XLINK_NS, 'href')).to.equal('#icon-star')
1589
+ expect(useViewBox.getAttributeNS(XLINK_NS, 'href'))
1590
+ .to.equal('#icon-star')
1327
1591
 
1328
- const svgWithClass = svgUse('icon-menu', { class: 'icon-btn' })
1592
+ const svgWithClass =
1593
+ svgUse('icon-menu', { class: 'icon-btn' })
1594
+
1595
+ expect(svgWithClass.getAttribute('class'))
1596
+ .to.equal('icon-btn')
1329
1597
 
1330
- expect(svgWithClass.getAttribute('class')).to.equal('icon-btn')
1331
1598
  expect(svgWithClass.children.length).to.equal(1)
1332
1599
 
1333
1600
  const useClass = svgWithClass.children[0]
1334
1601
 
1335
- expect(useClass.getAttributeNS(XLINK_NS, 'href')).to.equal('#icon-menu')
1602
+ expect(useClass.getAttributeNS(XLINK_NS, 'href'))
1603
+ .to.equal('#icon-menu')
1604
+
1605
+ const svgWithMultipleAttrs = svgUse(
1606
+ 'icon-settings',
1607
+ { width: 24, height: 24, class: 'icon' },
1608
+ )
1609
+
1610
+ expect(svgWithMultipleAttrs.getAttribute('width'))
1611
+ .to.equal('24')
1336
1612
 
1337
- const svgWithMultipleAttrs = svgUse('icon-settings', { width: 24, height: 24, class: 'icon' })
1613
+ expect(svgWithMultipleAttrs.getAttribute('height'))
1614
+ .to.equal('24')
1338
1615
 
1339
- expect(svgWithMultipleAttrs.getAttribute('width')).to.equal('24')
1340
- expect(svgWithMultipleAttrs.getAttribute('height')).to.equal('24')
1341
- expect(svgWithMultipleAttrs.getAttribute('class')).to.equal('icon')
1616
+ expect(svgWithMultipleAttrs.getAttribute('class'))
1617
+ .to.equal('icon')
1342
1618
 
1343
1619
  const useMultiple = svgWithMultipleAttrs.children[0]
1344
1620
 
1345
- expect(useMultiple.getAttributeNS(XLINK_NS, 'href')).to.equal('#icon-settings')
1621
+ expect(useMultiple.getAttributeNS(XLINK_NS, 'href'))
1622
+ .to.equal('#icon-settings')
1346
1623
  ```
1347
1624
 
1348
1625
  ### uuidV1
@@ -1355,16 +1632,21 @@ Generates a UUID v1 (time-based) identifier.
1355
1632
 
1356
1633
  #### date
1357
1634
 
1358
- The date to use for the timestamp portion (default: current date/time).
1635
+ The date to use for the timestamp portion
1636
+ (default: current date/time).
1359
1637
 
1360
1638
  #### node
1361
1639
 
1362
- A hexadecimal `string` for the node portion (default: random).
1363
- Must match `/^[0-9a-f]*$/`; it is trimmed to the last 12 characters and left-padded with zeros if shorter.
1640
+ A hexadecimal `string` for the node portion
1641
+ (default: random).
1642
+ Must match `/^[0-9a-f]*$/`;
1643
+ it is trimmed to the last 12 characters
1644
+ and left-padded with zeros if shorter.
1364
1645
 
1365
1646
  #### Returns
1366
1647
 
1367
- A UUID v1 `string` in the standard format `xxxxxxxx-xxxx-1xxx-xxxx-xxxxxxxxxxxx`.
1648
+ A UUID v1 `string` in the standard format
1649
+ `xxxxxxxx-xxxx-1xxx-xxxx-xxxxxxxxxxxx`.
1368
1650
 
1369
1651
  #### Usage Examples
1370
1652
 
@@ -1401,10 +1683,17 @@ for (let i = 1; i <= 22136; ++i) {
1401
1683
  }
1402
1684
  }
1403
1685
 
1404
- expect(uuidV1(new Date(), '000123456789abc').split('-')[4]).to.equal('123456789abc')
1405
- expect(uuidV1(new Date(), '123456789').split('-')[4]).to.equal('000123456789')
1686
+ expect(uuidV1(new Date(), '000123456789abc').split('-')[4])
1687
+ .to.equal('123456789abc')
1406
1688
 
1407
- expect(uuidV1(new Date(323325000000)).startsWith('c1399400-9a71-11bd')).to.be.true
1689
+ expect(uuidV1(new Date(), '123456789').split('-')[4])
1690
+ .to.equal('000123456789')
1691
+
1692
+ expect(
1693
+ uuidV1(new Date(323325000000))
1694
+ .startsWith('c1399400-9a71-11bd'),
1695
+ )
1696
+ .to.be.true
1408
1697
  ```
1409
1698
 
1410
1699
  ### vivify
@@ -1413,22 +1702,36 @@ expect(uuidV1(new Date(323325000000)).startsWith('c1399400-9a71-11bd')).to.be.tr
1413
1702
  const vivify: (ref: unknown) => any;
1414
1703
  ```
1415
1704
 
1416
- A Proxy-based helper for auto-vivification of nested object structures.
1705
+ A Proxy-based helper for auto-vivification
1706
+ of nested object structures.
1417
1707
 
1418
- Accessing, assigning, or deleting any nested property on the returned proxy automatically creates intermediate objects
1419
- (or arrays for numeric-string keys matching `^(0|[1-9]\d*)$`) as needed, allowing deep operations without explicit null checks.
1708
+ Accessing, assigning, or deleting any nested property
1709
+ on the returned proxy
1710
+ automatically creates intermediate objects
1711
+ (or arrays for numeric-string keys matching
1712
+ `^(0|[1-9]\d*)$`) as needed,
1713
+ allowing deep operations without explicit null checks.
1420
1714
 
1421
- Intermediates of the last level in a get-only property chain are NOT auto-created.
1422
- For example, `vivify(ref).one.two` will create `ref.one` as `{}`, but will NOT create `ref.one.two`.
1715
+ Intermediates of the last level
1716
+ in a get-only property chain are NOT auto-created.
1717
+ For example, `vivify(ref).one.two` will create
1718
+ `ref.one` as `{}`, but will NOT create `ref.one.two`.
1423
1719
  Only when a deeper access, assignment, or deletion occurs
1424
- (e.g. `delete vivify(ref).one.two.three` or `vivify(ref).one.two.three = 4`) will `ref.one.two` be materialized.
1720
+ (e.g. `delete vivify(ref).one.two.three`
1721
+ or `vivify(ref).one.two.three = 4`)
1722
+ will `ref.one.two` be materialized.
1425
1723
 
1426
- When traversal reaches a primitive value, no auto-creation happens;
1724
+ When traversal reaches a primitive value,
1725
+ no auto-creation happens;
1427
1726
  the primitive’s own property is returned instead
1428
- (e.g. accessing `.toString.name` on a number yields `'toString'` without modifying the underlying structure).
1727
+ (e.g. accessing `.toString.name` on a number yields
1728
+ `'toString'` without modifying the underlying structure).
1429
1729
 
1430
- Deletion on a non-existing intermediate path will auto-create intermediates up to the parent of the deleted key
1431
- (e.g. `delete vivify(ref).one.two.three` will create `ref.one.two` as `{}` if it does not exist).
1730
+ Deletion on a non-existing intermediate path
1731
+ will auto-create intermediates up to the parent
1732
+ of the deleted key
1733
+ (e.g. `delete vivify(ref).one.two.three`
1734
+ will create `ref.one.two` as `{}` if it does not exist).
1432
1735
 
1433
1736
  #### ref
1434
1737
 
@@ -1436,7 +1739,8 @@ The root object to wrap.
1436
1739
 
1437
1740
  #### Returns
1438
1741
 
1439
- A proxy that auto-creates nested objects/arrays on property access.
1742
+ A proxy that auto-creates nested objects/arrays
1743
+ on property access.
1440
1744
 
1441
1745
  #### Usage Examples
1442
1746
 
@@ -1445,43 +1749,58 @@ const ref: any = {}
1445
1749
 
1446
1750
  vivify(ref).one.two[3][4]
1447
1751
 
1448
- expect(ref).to.deep.equal({
1449
- one: {
1450
- two: [undefined, undefined, undefined, []],
1752
+ expect(ref).to.deep.equal(
1753
+ {
1754
+ one: {
1755
+ two: [undefined, undefined, undefined, []],
1756
+ },
1451
1757
  },
1452
- })
1758
+ )
1453
1759
 
1454
1760
  vivify(ref).one.two[3][4] = 5
1455
1761
 
1456
- expect(ref).to.deep.equal({
1457
- one: {
1458
- two: [undefined, undefined, undefined, [undefined, undefined, undefined, undefined, 5]],
1762
+ expect(ref).to.deep.equal(
1763
+ {
1764
+ one: {
1765
+ two: [
1766
+ undefined,
1767
+ undefined,
1768
+ undefined,
1769
+ [undefined, undefined, undefined, undefined, 5],
1770
+ ],
1771
+ },
1459
1772
  },
1460
- })
1773
+ )
1461
1774
 
1462
1775
  vivify(ref).one.two[3].length = 1
1463
1776
 
1464
- expect(ref).to.deep.equal({
1465
- one: {
1466
- two: [undefined, undefined, undefined, [undefined]],
1777
+ expect(ref).to.deep.equal(
1778
+ {
1779
+ one: {
1780
+ two: [undefined, undefined, undefined, [undefined]],
1781
+ },
1467
1782
  },
1468
- })
1783
+ )
1469
1784
 
1470
1785
  vivify(ref).one.two[3] = 4
1471
1786
 
1472
- expect(ref).to.deep.equal({
1473
- one: {
1474
- two: [undefined, undefined, undefined, 4],
1787
+ expect(ref).to.deep.equal(
1788
+ {
1789
+ one: {
1790
+ two: [undefined, undefined, undefined, 4],
1791
+ },
1475
1792
  },
1476
- })
1793
+ )
1477
1794
 
1478
1795
  expect(vivify(ref).one.two.length).to.equal(4)
1479
1796
 
1480
- expect(ref).to.deep.equal({
1481
- one: {
1482
- two: [undefined, undefined, undefined, 4],
1797
+ expect(ref).to.deep.equal(
1798
+ {
1799
+ one: {
1800
+ two: [undefined, undefined, undefined, 4],
1801
+ },
1483
1802
  },
1484
- })
1803
+ )
1485
1804
 
1486
1805
  vivify(ref).one.two = 3
1487
1806
 
@@ -1495,7 +1814,8 @@ delete vivify(ref).one.two
1495
1814
 
1496
1815
  expect(ref).to.deep.equal({ one: {} })
1497
1816
 
1498
- expect(vivify(ref).one.toString instanceof Function).to.be.true
1817
+ expect(vivify(ref).one.toString instanceof Function)
1818
+ .to.be.true
1499
1819
 
1500
1820
  expect(vivify(ref).one.toString.name).to.equal('toString')
1501
1821
 
@@ -1515,11 +1835,14 @@ expect(ref).to.deep.equal({ one: { two: { three: {} } } })
1515
1835
 
1516
1836
  vivify(ref).one.two.three.four = 5
1517
1837
 
1518
- expect(ref).to.deep.equal({ one: { two: { three: { four: 5 } } } })
1838
+ expect(ref)
1839
+ .to.deep.equal({ one: { two: { three: { four: 5 } } } })
1519
1840
 
1520
- expect(vivify(ref).one.two.three.four.toString.name).to.equal('toString')
1841
+ expect(vivify(ref).one.two.three.four.toString.name)
1842
+ .to.equal('toString')
1521
1843
 
1522
- expect(ref).to.deep.equal({ one: { two: { three: { four: 5 } } } })
1844
+ expect(ref)
1845
+ .to.deep.equal({ one: { two: { three: { four: 5 } } } })
1523
1846
 
1524
1847
  const u = undefined
1525
1848