@jackens/nnn 2026.4.15 → 2026.8.31

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 +52 -89
  2. package/nnn.js +81 -83
  3. package/package.json +1 -2
  4. package/readme.md +554 -591
package/readme.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # nnn
2
2
 
3
- A collection of Jackens’ JavaScript helper utilities (version: `2026.4.15`).
3
+ A collection of Jackens’ JavaScript helper utilities (version: `2026.8.31`).
4
4
 
5
5
  ## Installation
6
6
 
@@ -19,6 +19,8 @@ npm i @jackens/nnn
19
19
  ```js
20
20
  import {
21
21
  c,
22
+ createEscape,
23
+ createTokenizer,
22
24
  csvParse,
23
25
  fixPlTypography,
24
26
  h,
@@ -32,9 +34,6 @@ import {
32
34
  jsOnParse,
33
35
  monokai,
34
36
  nanolightTs,
35
- newEscape,
36
- newNounForm,
37
- newTokenizer,
38
37
  omit,
39
38
  pick,
40
39
  rwd,
@@ -61,6 +60,12 @@ import {
61
60
  A minimal CSS-in-JS helper
62
61
  that converts a JavaScript object hierarchy
63
62
  into a CSS string.
63
+ - [`createEscape`](#createEscape):
64
+ Creates a tag function
65
+ for escaping interpolated values in template literals.
66
+ - [`createTokenizer`](#createTokenizer):
67
+ A helper for building simple tokenizers
68
+ (see also [`nanolightTs`](#nanolightTs)).
64
69
  - [`csvParse`](#csvParse):
65
70
  Parses a CSV string
66
71
  into a two-dimensional array of strings.
@@ -98,17 +103,7 @@ import {
98
103
  and [`nanolightTs`](#nanolightTs) tokenizer.
99
104
  - [`nanolightTs`](#nanolightTs):
100
105
  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)).
106
+ built using [`createTokenizer`](#createTokenizer).
112
107
  - [`omit`](#omit):
113
108
  Creates a new object excluding the specified keys
114
109
  from the source object.
@@ -215,16 +210,14 @@ A CSS string representing the compiled rules.
215
210
  #### Usage Examples
216
211
 
217
212
  ```ts
218
- const actual1 = c(
219
- {
220
- a: {
221
- color: 'red',
222
- margin: 1,
223
- '.c': { margin: 2, padding: 2 },
224
- padding: 1,
225
- },
213
+ const actual1 = c({
214
+ a: {
215
+ color: 'red',
216
+ margin: 1,
217
+ '.c': { margin: 2, padding: 2 },
218
+ padding: 1,
226
219
  },
227
- )
220
+ })
228
221
 
229
222
  const expected1 = `
230
223
  a{
@@ -241,18 +234,16 @@ a{
241
234
 
242
235
  expect(actual1).to.equal(expected1)
243
236
 
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
- },
237
+ const actual2 = c({
238
+ a: {
239
+ '.b': {
240
+ color: 'red',
241
+ margin: 1,
242
+ '.c': { margin: 2, padding: 2 },
243
+ padding: 1,
253
244
  },
254
245
  },
255
- )
246
+ })
256
247
 
257
248
  const expected2 = `
258
249
  a.b{
@@ -269,36 +260,35 @@ a.b{
269
260
 
270
261
  expect(actual2).to.equal(expected2)
271
262
 
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')," +
263
+ const actual3 = c({
264
+ '@font-face$$1': {
265
+ fontFamily: 'Jackens',
266
+ src$$1: 'url(otf/jackens.otf)',
267
+ src$$2:
268
+ "url(otf/jackens.otf) format('opentype')," +
278
269
  "url(svg/jackens.svg) format('svg')",
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
- },
270
+ font_weight: 'normal',
271
+ 'font-style': 'normal',
300
272
  },
301
- )
273
+ '@font-face$$2': {
274
+ font_family: 'C64',
275
+ src: 'url(fonts/C64_Pro_Mono-STYLE.woff)',
276
+ },
277
+ '@keyframes spin': {
278
+ '0%': { transform: 'rotate(0deg)' },
279
+ '100%': { transform: 'rotate(360deg)' },
280
+ },
281
+ div: {
282
+ border: 'solid red 1px',
283
+ '.c1': { 'background-color': '#000' },
284
+ ' .c1': { background_color: 'black' },
285
+ '.c2': { backgroundColor: 'rgb(0,0,0)' },
286
+ },
287
+ '@media(min-width:200px)': {
288
+ div: { margin: 0, padding: 0 },
289
+ span: { color: '#000' },
290
+ },
291
+ })
302
292
 
303
293
  const expected3 = `
304
294
  @font-face{
@@ -344,18 +334,16 @@ div.c2{
344
334
 
345
335
  expect(actual3).to.equal(expected3)
346
336
 
347
- const actual4 = c(
348
- {
349
- a: {
350
- '.b,.c': {
351
- margin: 1,
352
- '.d': {
353
- margin: 2,
354
- },
337
+ const actual4 = c({
338
+ a: {
339
+ '.b,.c': {
340
+ margin: 1,
341
+ '.d': {
342
+ margin: 2,
355
343
  },
356
344
  },
357
345
  },
358
- )
346
+ })
359
347
 
360
348
  const expected4 = `
361
349
  a.b,a.c{
@@ -367,16 +355,14 @@ a.b.d,a.c.d{
367
355
 
368
356
  expect(actual4).to.equal(expected4)
369
357
 
370
- const actual5 = c(
371
- {
372
- '.b,.c': {
373
- margin: 1,
374
- '.d': {
375
- margin: 2,
376
- },
358
+ const actual5 = c({
359
+ '.b,.c': {
360
+ margin: 1,
361
+ '.d': {
362
+ margin: 2,
377
363
  },
378
364
  },
379
- )
365
+ })
380
366
 
381
367
  const expected5 = `
382
368
  .b,.c{
@@ -388,16 +374,14 @@ const expected5 = `
388
374
 
389
375
  expect(actual5).to.equal(expected5)
390
376
 
391
- const actual6 = c(
392
- {
393
- '.a,.b': {
394
- margin: 1,
395
- '.c,.d': {
396
- margin: 2,
397
- },
377
+ const actual6 = c({
378
+ '.a,.b': {
379
+ margin: 1,
380
+ '.c,.d': {
381
+ margin: 2,
398
382
  },
399
383
  },
400
- )
384
+ })
401
385
 
402
386
  const expected6 = `
403
387
  .a,.b{
@@ -410,6 +394,221 @@ const expected6 = `
410
394
  expect(actual6).to.equal(expected6)
411
395
  ```
412
396
 
397
+ ### createEscape
398
+
399
+ ```ts
400
+ const createEscape: (escapeFn: (value: any) => string) => (template: TemplateStringsArray, ...values: unknown[]) => string;
401
+ ```
402
+
403
+ Creates a tag function
404
+ for escaping interpolated values in template literals.
405
+
406
+ #### escapeFn
407
+
408
+ A function that takes a value
409
+ and returns its escaped string representation.
410
+
411
+ #### Returns
412
+
413
+ A tag function that escapes interpolated values
414
+ using the provided escape map.
415
+
416
+ #### Usage Examples
417
+
418
+ ```ts
419
+ const escapeFn = (value: any): string =>
420
+ isArray(value)
421
+ ? value.map(escapeFn).join(', ')
422
+ : value === true || value === false
423
+ ? `b'${+value}'`
424
+ : value instanceof Date
425
+ ? `'${value
426
+ .toISOString()
427
+ .replace(/^(.+)T(.+)\..*$/, '$1 $2')}'`
428
+ : isFiniteNumber(value)
429
+ ? `${value}`
430
+ : isString(value)
431
+ ? `'${value.replace(/'/g, "''")}'`
432
+ : 'NULL'
433
+
434
+ const sql = createEscape(escapeFn)
435
+
436
+ const actual = sql`
437
+ SELECT *
438
+ FROM table_name
439
+ WHERE column_name IN (
440
+ ${[
441
+ true,
442
+ null,
443
+ undefined,
444
+ NaN,
445
+ Infinity,
446
+ 42,
447
+ '42',
448
+ "4'2",
449
+ /42/,
450
+ new Date(323325000000),
451
+ ]}
452
+ )`
453
+
454
+ const expected = `
455
+ SELECT *
456
+ FROM table_name
457
+ WHERE column_name IN (
458
+ b'1', NULL, NULL, NULL, NULL, 42, '42', '4''2', NULL, '1980-03-31 04:30:00'
459
+ )`
460
+
461
+ expect(actual).to.equal(expected)
462
+ ```
463
+
464
+ ### createTokenizer
465
+
466
+ ```ts
467
+ const createTokenizer: <M, T>(decorator: (chunk: string, metadata?: M) => T, ...specs: [M, string | RegExp][]) => (code: string) => T[];
468
+ ```
469
+
470
+ A helper for building simple tokenizers
471
+ (see also [`nanolightTs`](#nanolightTs)).
472
+
473
+ #### decorator
474
+
475
+ A function that wraps each matched chunk.
476
+ It receives the matched text (`chunk`)
477
+ and optionally the `metadata`
478
+ associated with the pattern that produced the match.
479
+ For unmatched text between patterns,
480
+ `metadata` is `undefined`.
481
+
482
+ #### specs
483
+
484
+ An array of tuples `[metadata, pattern]` where:
485
+ - `metadata`: arbitrary data (e.g., a CSS class name)
486
+ passed to `decorator` when the pattern matches.
487
+ - `pattern`: a `string` or `RegExp`
488
+ to match against the input.
489
+
490
+ #### Returns
491
+
492
+ A tokenizer function that accepts a code string
493
+ and returns an array of decorated tokens.
494
+
495
+ #### Remarks
496
+
497
+ 1. Matches starting at an earlier position
498
+ take precedence.
499
+ 2. Among matches at the same position,
500
+ the longer one wins.
501
+ 3. Among matches of the same position and length,
502
+ the one defined earlier wins.
503
+
504
+ #### Usage Examples
505
+
506
+ ```ts
507
+ const tokenizer1 = createTokenizer(
508
+ (chunk, metadata) => ({ chunk, metadata }),
509
+ ['keyword', /\b(if|else|for)\b/],
510
+ ['string', /"[^"]*"/]
511
+ )
512
+
513
+ const result1 = tokenizer1('if "hello" else "world"')
514
+
515
+ expect(result1).to.deep.equal([
516
+ { chunk: 'if', metadata: 'keyword' },
517
+ { chunk: ' ', metadata: undefined },
518
+ { chunk: '"hello"', metadata: 'string' },
519
+ { chunk: ' ', metadata: undefined },
520
+ { chunk: 'else', metadata: 'keyword' },
521
+ { chunk: ' ', metadata: undefined },
522
+ { chunk: '"world"', metadata: 'string' },
523
+ ])
524
+
525
+ const tokenizer2 = createTokenizer(
526
+ (chunk, metadata) => `${metadata}:${chunk}`,
527
+ ['tag', 'BEGIN'],
528
+ ['end', 'END']
529
+ )
530
+
531
+ const result2 = tokenizer2('aBEGINbENDc')
532
+
533
+ expect(result2).to.deep.equal([
534
+ 'undefined:a',
535
+ 'tag:BEGIN',
536
+ 'undefined:b',
537
+ 'end:END',
538
+ 'undefined:c',
539
+ ])
540
+
541
+ const tokenizer3 = createTokenizer(
542
+ chunk => chunk,
543
+ ['test', /test/]
544
+ )
545
+
546
+ const result3 = tokenizer3('')
547
+
548
+ expect(result3).to.deep.equal([])
549
+
550
+ const tokenizer4 = createTokenizer(
551
+ (chunk, metadata) => ({ chunk, metadata }),
552
+ ['start', /^test/]
553
+ )
554
+
555
+ const result4 = tokenizer4('test here')
556
+
557
+ expect(result4).to.deep.equal([
558
+ { chunk: 'test', metadata: 'start' },
559
+ { chunk: ' here', metadata: undefined },
560
+ ])
561
+
562
+ const tokenizer5 = createTokenizer(
563
+ (chunk, metadata) => metadata,
564
+ ['later', /x/],
565
+ ['earlier', /y/]
566
+ )
567
+
568
+ const result5 = tokenizer5('yx')
569
+
570
+ expect(result5).to.deep.equal(['earlier', 'later'])
571
+
572
+ const tokenizer6 = createTokenizer(
573
+ chunk => chunk,
574
+ ['short', 'a'],
575
+ ['long', 'abc']
576
+ )
577
+
578
+ const result6 = tokenizer6('abc')
579
+
580
+ expect(result6).to.deep.equal(['abc'])
581
+
582
+ const tokenizer7 = createTokenizer(
583
+ chunk => chunk,
584
+ ['empty', ''],
585
+ ['word', /\w+/]
586
+ )
587
+
588
+ const result7 = tokenizer7('hello')
589
+
590
+ expect(result7).to.deep.equal(['hello'])
591
+
592
+ const tokenizer8 = createTokenizer(
593
+ chunk => chunk,
594
+ ['test', /xyz/]
595
+ )
596
+
597
+ const result8 = tokenizer8('abc')
598
+
599
+ expect(result8).to.deep.equal(['abc'])
600
+
601
+ const tokenizer9 = createTokenizer(
602
+ (_chunk, metadata) => metadata,
603
+ ['a', 'a'],
604
+ ['b', 'b']
605
+ )
606
+
607
+ const result9 = tokenizer9('aabb')
608
+
609
+ expect(result9).to.deep.equal(['a', 'a', 'b', 'b'])
610
+ ```
611
+
413
612
  ### csvParse
414
613
 
415
614
  ```ts
@@ -447,13 +646,11 @@ yyy",zzz
447
646
 
448
647
  `
449
648
 
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
- )
649
+ expect(csvParse(text)).to.deep.equal([
650
+ ['aaa\n"aaa"\naaa', 'bbb', 'ccc,ccc'],
651
+ ['xxx,xxx', 'yyy\nyyy', 'zzz'],
652
+ [' 42 ', '42', ' 17'],
653
+ ])
457
654
  ```
458
655
 
459
656
  ### fixPlTypography
@@ -483,23 +680,25 @@ except those inside `IFRAME`, `NOSCRIPT`, `PRE`,
483
680
  #### Usage Examples
484
681
 
485
682
  ```ts
486
- const p = h('p',
683
+ const p = h(
684
+ 'p',
487
685
  'Pchnąć w tę łódź jeża lub ośm skrzyń fig ' +
488
- '(zob. https://pl.wikipedia.org/wiki/Pangram).',
686
+ '(zob. https://pl.wikipedia.org/wiki/Pangram).',
489
687
  ['br'],
490
- ['b', 'Zażółć gęślą jaźń.'],
688
+ ['b', 'Zażółć gęślą jaźń.']
491
689
  )
492
690
 
493
691
  fixPlTypography(p)
494
692
 
495
693
  expect(p.innerHTML).to.deep.equal(
496
694
  'Pchnąć ' +
497
- '<span style="white-space:nowrap">w </span>' +
498
- 'tę łó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>')
695
+ '<span style="white-space:nowrap">w </span>' +
696
+ 'tę łódź jeża lub ośm skrzyń fig ' +
697
+ '(zob. https://\u200Bpl.\u200Bwikipedia.\u200Borg/' +
698
+ '\u200Bwiki/\u200BPangram).' +
699
+ '<br>' +
700
+ '<b>Zażółć gęślą jaźń.</b>'
701
+ )
503
702
  ```
504
703
 
505
704
  ### h
@@ -552,35 +751,43 @@ const i = h('i', 'text')
552
751
  h(b, i)
553
752
 
554
753
  expect(i.outerHTML).to.equal('<i>text</i>')
754
+
555
755
  expect(b.outerHTML).to.equal('<b><i>text</i></b>')
556
756
 
557
757
  h(i, { $className: 'some class' })
558
758
 
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>')
759
+ expect(i.outerHTML).to.equal(
760
+ '<i class="some class">text</i>'
761
+ )
762
+
763
+ expect(b.outerHTML).to.equal(
764
+ '<b><i class="some class">text</i></b>'
765
+ )
766
+
767
+ expect(h('span', 'text').outerHTML).to.equal(
768
+ '<span>text</span>'
769
+ )
770
+
771
+ expect(
772
+ h('span', { $innerText: 'text' }).outerHTML
773
+ ).to.equal('<span>text</span>')
563
774
 
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>')
775
+ expect(h('span', '42').outerHTML).to.equal(
776
+ '<span>42</span>'
777
+ )
568
778
 
569
- expect(h('span', '42').outerHTML)
570
- .to.equal('<span>42</span>')
571
779
  expect(h('span', 42).outerHTML).to.equal('<span>42</span>')
572
780
 
573
- expect(h('div', { style: 'margin:0;padding:0' }).outerHTML)
574
- .to.equal('<div style="margin:0;padding:0"></div>')
575
- expect(h('div', { $style: 'margin:0;padding:0' }).outerHTML)
576
- .to.equal(
577
- '<div style="margin: 0px; padding: 0px;"></div>',
578
- )
579
781
  expect(
580
- h(
581
- 'div',
582
- { $style: { margin: 0, padding: 0 } },
583
- ).outerHTML,
782
+ h('div', { style: 'margin:0;padding:0' }).outerHTML
783
+ ).to.equal('<div style="margin:0;padding:0"></div>')
784
+
785
+ expect(
786
+ h('div', { $style: 'margin:0;padding:0' }).outerHTML
787
+ ).to.equal('<div style="margin: 0px; padding: 0px;"></div>')
788
+
789
+ expect(
790
+ h('div', { $style: { margin: 0, padding: 0 } }).outerHTML
584
791
  ).to.equal('<div style="margin: 0px; padding: 0px;"></div>')
585
792
 
586
793
  const input1 = h('input', { value: 42 })
@@ -592,22 +799,26 @@ expect(input2.value).to.equal('42')
592
799
  expect(input1.outerHTML).to.equal('<input value="42">')
593
800
  expect(input2.outerHTML).to.equal('<input>')
594
801
 
595
- const checkbox1 = h(
596
- 'input',
597
- { type: 'checkbox', checked: true },
598
- )
599
- const checkbox2 = h(
600
- 'input',
601
- { type: 'checkbox', $checked: true },
602
- )
802
+ const checkbox1 = h('input', {
803
+ type: 'checkbox',
804
+ checked: true,
805
+ })
806
+
807
+ const checkbox2 = h('input', {
808
+ type: 'checkbox',
809
+ $checked: true,
810
+ })
603
811
 
604
812
  expect(checkbox1.checked).to.be.true
605
813
  expect(checkbox2.checked).to.be.true
606
814
 
607
- expect(checkbox1.outerHTML)
608
- .to.equal('<input type="checkbox" checked="">')
609
- expect(checkbox2.outerHTML)
610
- .to.equal('<input type="checkbox">')
815
+ expect(checkbox1.outerHTML).to.equal(
816
+ '<input type="checkbox" checked="">'
817
+ )
818
+
819
+ expect(checkbox2.outerHTML).to.equal(
820
+ '<input type="checkbox">'
821
+ )
611
822
 
612
823
  const div = h('div')
613
824
 
@@ -628,15 +839,20 @@ expect(elemWithClass.getAttribute('class')).to.equal('test')
628
839
  const elemWithText = h('div', 'initial')
629
840
 
630
841
  h(elemWithText, ' more')
631
- expect(elemWithText.outerHTML)
632
- .to.equal('<div>initial more</div>')
633
842
 
634
- const elemWithNested = h('div',
843
+ expect(elemWithText.outerHTML).to.equal(
844
+ '<div>initial more</div>'
845
+ )
846
+
847
+ const elemWithNested = h(
848
+ 'div',
635
849
  ['span', 'hello'],
636
- ['b', 'world'])
850
+ ['b', 'world']
851
+ )
637
852
 
638
- expect(elemWithNested.outerHTML)
639
- .to.equal('<div><span>hello</span><b>world</b></div>')
853
+ expect(elemWithNested.outerHTML).to.equal(
854
+ '<div><span>hello</span><b>world</b></div>'
855
+ )
640
856
  ```
641
857
 
642
858
  ### hasOwn
@@ -721,8 +937,9 @@ const fakeArray1 = Object.create({ constructor: Array })
721
937
 
722
938
  expect(isArray(fakeArray1)).to.be.false
723
939
 
724
- const fakeArray2 =
725
- Object.create({ [Symbol.toStringTag]: Array.name })
940
+ const fakeArray2 = Object.create({
941
+ [Symbol.toStringTag]: Array.name,
942
+ })
726
943
 
727
944
  expect(isArray(fakeArray2)).to.be.false
728
945
  ```
@@ -748,9 +965,13 @@ The value to check.
748
965
 
749
966
  ```ts
750
967
  expect(isFiniteNumber(42)).to.be.true
968
+
751
969
  expect(isFiniteNumber(Number(42))).to.be.true
970
+
752
971
  expect(isFiniteNumber(new Number(42))).to.be.false
972
+
753
973
  expect(isFiniteNumber(NaN)).to.be.false
974
+
754
975
  expect(isFiniteNumber(Infinity)).to.be.false
755
976
  ```
756
977
 
@@ -774,12 +995,18 @@ The value to check.
774
995
 
775
996
  ```ts
776
997
  expect(isInteger(42)).to.be.true
998
+
777
999
  expect(isInteger(42.00000000000001)).to.be.false
1000
+
778
1001
  expect(isInteger(42.000000000000001)).to.be.true
779
1002
  // ^because of loss of precision
1003
+
780
1004
  expect(isInteger(Number(42))).to.be.true
1005
+
781
1006
  expect(isInteger(new Number(42))).to.be.false
1007
+
782
1008
  expect(isInteger(NaN)).to.be.false
1009
+
783
1010
  expect(isInteger(Infinity)).to.be.false
784
1011
  ```
785
1012
 
@@ -804,9 +1031,13 @@ The value to check.
804
1031
 
805
1032
  ```ts
806
1033
  expect(isNumber(42)).to.be.true
1034
+
807
1035
  expect(isNumber(Number(42))).to.be.true
1036
+
808
1037
  expect(isNumber(new Number(42))).to.be.false
1038
+
809
1039
  expect(isNumber(NaN)).to.be.true
1040
+
810
1041
  expect(isNumber(Infinity)).to.be.true
811
1042
  ```
812
1043
 
@@ -831,22 +1062,26 @@ The value to check.
831
1062
 
832
1063
  ```ts
833
1064
  expect(isRecord({})).to.be.true
1065
+
834
1066
  expect(isRecord([])).to.be.false
1067
+
835
1068
  expect(isRecord(Object.create(null))).to.be.true
836
1069
 
837
1070
  const fakeNumber1 = Object.create({ constructor: Number })
838
1071
 
839
1072
  expect(isRecord(fakeNumber1)).to.be.true
840
1073
 
841
- const fakeNumber2 =
842
- Object.create({ [Symbol.toStringTag]: Number.name })
1074
+ const fakeNumber2 = Object.create({
1075
+ [Symbol.toStringTag]: Number.name,
1076
+ })
843
1077
 
844
1078
  expect(isRecord(fakeNumber2)).to.be.true
845
1079
 
846
1080
  expect(isRecord(new Number(42))).to.be.true
1081
+
847
1082
  expect(isRecord(new String('42'))).to.be.true
848
1083
 
849
- class FooBar { }
1084
+ class FooBar {}
850
1085
 
851
1086
  expect(isRecord(new FooBar())).to.be.true
852
1087
  ```
@@ -911,7 +1146,9 @@ const handlers = {
911
1146
  $foo: () => 'bar',
912
1147
  }
913
1148
 
914
- const actual1 = jsOnParse(handlers, `[
1149
+ const actual1 = jsOnParse(
1150
+ handlers,
1151
+ `[
915
1152
  {
916
1153
  "$add": [1, 2]
917
1154
  },
@@ -933,51 +1170,50 @@ const actual1 = jsOnParse(handlers, `[
933
1170
  "one": 1,
934
1171
  "two": 2
935
1172
  }
936
- ]`)
1173
+ ]`
1174
+ )
937
1175
 
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 ' +
1176
+ expect(actual1).to.deep.equal([
1177
+ 3,
1178
+ 'Hello World!',
1179
+ {
1180
+ nested: 'Hello nested World!',
1181
+ one: 1,
1182
+ two: 2,
1183
+ },
1184
+ 'bar',
1185
+ {
1186
+ $foo: [
1187
+ 'The parent object does not have ' +
951
1188
  'exactly one property!',
952
- ],
953
- one: 1,
954
- two: 2,
955
- },
956
- ],
957
- )
1189
+ ],
1190
+ one: 1,
1191
+ two: 2,
1192
+ },
1193
+ ])
958
1194
 
959
1195
  const actual2 = jsOnParse(
960
1196
  { $notFunc: 'handler not being a function' } as any,
961
- '{"$notFunc": [1, 2]}',
1197
+ '{"$notFunc": [1, 2]}'
962
1198
  )
963
1199
 
964
1200
  expect(actual2).to.deep.equal({ $notFunc: [1, 2] })
965
1201
 
966
1202
  const actual3 = jsOnParse(
967
1203
  handlers,
968
- '{"$unknown_handler_key": [1, 2]}',
1204
+ '{"$unknown_handler_key": [1, 2]}'
969
1205
  )
970
1206
 
971
- expect(actual3)
972
- .to.deep.equal({ $unknown_handler_key: [1, 2] })
1207
+ expect(actual3).to.deep.equal({
1208
+ $unknown_handler_key: [1, 2],
1209
+ })
973
1210
 
974
1211
  const actual4 = jsOnParse(
975
1212
  handlers,
976
- '{"$add": {"not": "array"}}',
1213
+ '{"$add": {"not": "array"}}'
977
1214
  )
978
1215
 
979
- expect(actual4)
980
- .to.deep.equal({ $add: { not: 'array' } })
1216
+ expect(actual4).to.deep.equal({ $add: { not: 'array' } })
981
1217
  ```
982
1218
 
983
1219
  ### monokai
@@ -997,7 +1233,7 @@ const nanolightTs: (code: string) => HArgs1[];
997
1233
  ```
998
1234
 
999
1235
  A TypeScript/JavaScript syntax highlighting tokenizer
1000
- built using [`newTokenizer`](#newTokenizer).
1236
+ built using [`createTokenizer`](#createTokenizer).
1001
1237
 
1002
1238
  #### code
1003
1239
 
@@ -1015,311 +1251,31 @@ const codeJs =
1015
1251
  'const answerToLifeTheUniverseAndEverything = ' +
1016
1252
  "{ 42: 42 }['42'] /* 42 */"
1017
1253
 
1018
- expect(nanolightTs(codeJs)).to.deep.equal(
1254
+ expect(nanolightTs(codeJs)).to.deep.equal([
1255
+ ['span', { class: 'keyword-1' }, 'const'],
1256
+ ' ',
1019
1257
  [
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 */'],
1258
+ 'span',
1259
+ { class: 'identifier-4' },
1260
+ 'answerToLifeTheUniverseAndEverything',
1042
1261
  ],
1043
- )
1044
- ```
1045
-
1046
- ### newEscape
1047
-
1048
- ```ts
1049
- const newEscape: (escapeFn: (value: any) => string) => (template: TemplateStringsArray, ...values: unknown[]) => string;
1050
- ```
1051
-
1052
- Creates a tag function
1053
- for escaping interpolated values in template literals.
1054
-
1055
- #### escapeFn
1056
-
1057
- A function that takes a value
1058
- and returns its escaped string representation.
1059
-
1060
- #### Returns
1061
-
1062
- A tag function that escapes interpolated values
1063
- using the provided escape map.
1064
-
1065
- #### Usage Examples
1066
-
1067
- ```ts
1068
- const escapeFn = (value: any): string =>
1069
- isArray(value)
1070
- ? value.map(escapeFn).join(', ')
1071
- : value === true || value === false
1072
- ? `b'${+value}'`
1073
- : value instanceof Date
1074
- ? `'${value.toISOString().replace(/^(.+)T(.+)\..*$/, '$1 $2')}'`
1075
- : isFiniteNumber(value)
1076
- ? `${value}`
1077
- : isString(value)
1078
- ? `'${value.replace(/'/g, "''")}'`
1079
- : 'NULL'
1080
-
1081
- const sql = newEscape(escapeFn)
1082
-
1083
- const actual = sql`
1084
- SELECT *
1085
- FROM table_name
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
- )`
1100
-
1101
- const expected = `
1102
- SELECT *
1103
- FROM table_name
1104
- WHERE column_name IN (
1105
- b'1', NULL, NULL, NULL, NULL, 42, '42', '4''2', NULL, '1980-03-31 04:30:00'
1106
- )`
1107
-
1108
- expect(actual).to.equal(expected)
1109
- ```
1110
-
1111
- ### newNounForm
1112
-
1113
- ```ts
1114
- const newNounForm: (locale: string, forms: Partial<Record<Intl.LDMLPluralRule, string>>) => (value: number) => string;
1115
- ```
1116
-
1117
- Creates a function that
1118
- returns the appropriate noun form
1119
- based on a numeric value using `Intl.PluralRules`.
1120
-
1121
- Different languages have different plural rules.
1122
- The `Intl.PluralRules` API provides locale-aware
1123
- plural category selection.
1124
- Possible categories are:
1125
-
1126
- - `zero`: for zero items
1127
- (used in some languages like Arabic, Latvian)
1128
- - `one`: for singular (e.g., 1 item)
1129
- - `two`: for dual
1130
- (used in some languages like Arabic, Hebrew)
1131
- - `few`: for small plurals (e.g., 2-4 in Polish)
1132
- - `many`: for larger plurals (e.g., 5-21 in Polish)
1133
- - `other`: fallback category (used by all languages)
1134
-
1135
- #### locale
1136
-
1137
- A BCP 47 language tag (e.g., `pl`, `en`).
1138
-
1139
- #### forms
1140
-
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.
1146
-
1147
- #### Returns
1148
-
1149
- A function that takes a numeric value
1150
- and returns the appropriate noun form.
1151
-
1152
- #### Usage Examples
1153
-
1154
- ```ts
1155
- const auto = newNounForm(
1156
- 'pl',
1157
- { one: 'auto', few: 'auta', other: 'aut' },
1158
- )
1159
-
1160
- expect(auto(0)).to.equal('aut')
1161
- expect(auto(1)).to.equal('auto')
1162
- expect(auto(17)).to.equal('aut')
1163
- expect(auto(42)).to.equal('auta')
1164
-
1165
- const car = newNounForm('en', { one: 'car', other: 'cars' })
1166
-
1167
- expect(car(0)).to.equal('cars')
1168
- expect(car(1)).to.equal('car')
1169
- expect(car(17)).to.equal('cars')
1170
- expect(car(42)).to.equal('cars')
1171
-
1172
- const empty = newNounForm('en', {})
1173
-
1174
- expect(empty(0)).to.equal('')
1175
- expect(empty(1)).to.equal('')
1176
- expect(empty(17)).to.equal('')
1177
- expect(empty(42)).to.equal('')
1178
- ```
1179
-
1180
- ### newTokenizer
1181
-
1182
- ```ts
1183
- const newTokenizer: <M, T>(decorator: (chunk: string, metadata?: M) => T, ...specs: [M, string | RegExp][]) => (code: string) => T[];
1184
- ```
1185
-
1186
- A helper for building simple tokenizers
1187
- (see also [`nanolightTs`](#nanolightTs)).
1188
-
1189
- #### decorator
1190
-
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`.
1197
-
1198
- #### specs
1199
-
1200
- An array of tuples `[metadata, pattern]` where:
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.
1205
-
1206
- #### Returns
1207
-
1208
- A tokenizer function that accepts a code string
1209
- and returns an array of decorated tokens.
1210
-
1211
- #### Remarks
1212
-
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.
1219
-
1220
- #### Usage Examples
1221
-
1222
- ```ts
1223
- const tokenizer1 = newTokenizer(
1224
- (chunk, metadata) => ({ chunk, metadata }),
1225
- ['keyword', /\b(if|else|for)\b/],
1226
- ['string', /"[^"]*"/],
1227
- )
1228
- const result1 = tokenizer1('if "hello" else "world"')
1229
-
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
- )
1241
-
1242
- const tokenizer2 = newTokenizer(
1243
- (chunk, metadata) => `${metadata}:${chunk}`,
1244
- ['tag', 'BEGIN'],
1245
- ['end', 'END'],
1246
- )
1247
- const result2 = tokenizer2('aBEGINbENDc')
1248
-
1249
- expect(result2).to.deep.equal(
1250
- [
1251
- 'undefined:a',
1252
- 'tag:BEGIN',
1253
- 'undefined:b',
1254
- 'end:END',
1255
- 'undefined:c',
1256
- ],
1257
- )
1258
-
1259
- const tokenizer3 = newTokenizer(
1260
- (chunk) => chunk,
1261
- ['test', /test/],
1262
- )
1263
- const result3 = tokenizer3('')
1264
-
1265
- expect(result3).to.deep.equal([])
1266
-
1267
- const tokenizer4 = newTokenizer(
1268
- (chunk, metadata) => ({ chunk, metadata }),
1269
- ['start', /^test/],
1270
- )
1271
- const result4 = tokenizer4('test here')
1272
-
1273
- expect(result4).to.deep.equal(
1274
- [
1275
- { chunk: 'test', metadata: 'start' },
1276
- { chunk: ' here', metadata: undefined },
1277
- ],
1278
- )
1279
-
1280
- const tokenizer5 = newTokenizer(
1281
- (chunk, metadata) => metadata,
1282
- ['later', /x/],
1283
- ['earlier', /y/],
1284
- )
1285
- const result5 = tokenizer5('yx')
1286
-
1287
- expect(result5).to.deep.equal(['earlier', 'later'])
1288
-
1289
- const tokenizer6 = newTokenizer(
1290
- (chunk) => chunk,
1291
- ['short', 'a'],
1292
- ['long', 'abc'],
1293
- )
1294
- const result6 = tokenizer6('abc')
1295
-
1296
- expect(result6).to.deep.equal(['abc'])
1297
-
1298
- const tokenizer7 = newTokenizer(
1299
- (chunk) => chunk,
1300
- ['empty', ''],
1301
- ['word', /\w+/],
1302
- )
1303
- const result7 = tokenizer7('hello')
1304
-
1305
- expect(result7).to.deep.equal(['hello'])
1306
-
1307
- const tokenizer8 = newTokenizer(
1308
- (chunk) => chunk,
1309
- ['test', /xyz/],
1310
- )
1311
- const result8 = tokenizer8('abc')
1312
-
1313
- expect(result8).to.deep.equal(['abc'])
1314
-
1315
- const tokenizer9 = newTokenizer(
1316
- (_chunk, metadata) => metadata,
1317
- ['a', 'a'],
1318
- ['b', 'b'],
1319
- )
1320
- const result9 = tokenizer9('aabb')
1321
-
1322
- expect(result9).to.deep.equal(['a', 'a', 'b', 'b'])
1262
+ ' ',
1263
+ ['span', { class: 'operator' }, '='],
1264
+ ' ',
1265
+ ['span', { class: 'punctuation' }, '{'],
1266
+ ' ',
1267
+ ['span', { class: 'number' }, '42'],
1268
+ ['span', { class: 'operator' }, ':'],
1269
+ ' ',
1270
+ ['span', { class: 'number' }, '42'],
1271
+ ' ',
1272
+ ['span', { class: 'punctuation' }, '}'],
1273
+ ['span', { class: 'punctuation' }, '['],
1274
+ ['span', { class: 'string' }, "'42'"],
1275
+ ['span', { class: 'punctuation' }, ']'],
1276
+ ' ',
1277
+ ['span', { class: 'comment' }, '/* 42 */'],
1278
+ ])
1323
1279
  ```
1324
1280
 
1325
1281
  ### omit
@@ -1385,9 +1341,10 @@ A new object with only the specified keys.
1385
1341
  ```ts
1386
1342
  const ref = { a: 42, b: '42', c: 17 }
1387
1343
 
1388
- expect(pick(ref, ['a', 'b'])).to.deep.equal(
1389
- { a: 42, b: '42' },
1390
- )
1344
+ expect(pick(ref, ['a', 'b'])).to.deep.equal({
1345
+ a: 42,
1346
+ b: '42',
1347
+ })
1391
1348
  ```
1392
1349
 
1393
1350
  ### rwd
@@ -1442,36 +1399,34 @@ const style: CRoot = {
1442
1399
 
1443
1400
  rwd(style, '.r6', 200, 50, [6], [3], [1, 1, 2])
1444
1401
 
1445
- expect(style).to.deep.equal(
1446
- {
1447
- body: {
1448
- margin: 0,
1402
+ expect(style).to.deep.equal({
1403
+ body: {
1404
+ margin: 0,
1405
+ },
1406
+ '.r6': {
1407
+ border: 'solid red 1px',
1408
+ '.no-border': {
1409
+ border: 'none',
1449
1410
  },
1411
+ boxSizing: 'border-box',
1412
+ display: 'block',
1413
+ float: 'left',
1414
+ width: '100%',
1415
+ height: '100px',
1416
+ },
1417
+ '@media(min-width:600px)': {
1450
1418
  '.r6': {
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',
1460
- },
1461
- '@media(min-width:600px)': {
1462
- '.r6': {
1463
- width: 'calc(100% / 3)',
1464
- height: '50px',
1465
- },
1419
+ width: 'calc(100% / 3)',
1420
+ height: '50px',
1466
1421
  },
1467
- '@media(min-width:1200px)': {
1468
- '.r6': {
1469
- width: 'calc(50% / 3)',
1470
- height: '50px',
1471
- },
1422
+ },
1423
+ '@media(min-width:1200px)': {
1424
+ '.r6': {
1425
+ width: 'calc(50% / 3)',
1426
+ height: '50px',
1472
1427
  },
1473
1428
  },
1474
- )
1429
+ })
1475
1430
  ```
1476
1431
 
1477
1432
  ### s
@@ -1525,18 +1480,19 @@ const svg2 = s('svg', { 'xlink:href': false })
1525
1480
 
1526
1481
  expect(svg2.getAttributeNS(XLINK_NS, 'href')).to.be.null
1527
1482
 
1528
- const svg3 = s(
1529
- 'svg',
1530
- { 'xlink:href': 'http://example.com' },
1531
- )
1483
+ const svg3 = s('svg', {
1484
+ 'xlink:href': 'http://example.com',
1485
+ })
1532
1486
 
1533
- expect(svg3.getAttributeNS(XLINK_NS, 'href'))
1534
- .to.equal('http://example.com')
1487
+ expect(svg3.getAttributeNS(XLINK_NS, 'href')).to.equal(
1488
+ 'http://example.com'
1489
+ )
1535
1490
 
1536
1491
  const svg4 = s('svg', { 'xlink:title': 42 })
1537
1492
 
1538
- expect(svg4.getAttributeNS(XLINK_NS, 'title'))
1539
- .to.equal('42')
1493
+ expect(svg4.getAttributeNS(XLINK_NS, 'title')).to.equal(
1494
+ '42'
1495
+ )
1540
1496
  ```
1541
1497
 
1542
1498
  ### svgUse
@@ -1575,51 +1531,63 @@ expect(svg.children.length).to.equal(1)
1575
1531
 
1576
1532
  const useElement = svg.children[0]
1577
1533
 
1578
- expect(useElement.getAttributeNS(XLINK_NS, 'href'))
1579
- .to.equal('#icon-home')
1534
+ expect(
1535
+ useElement.getAttributeNS(XLINK_NS, 'href')
1536
+ ).to.equal('#icon-home')
1580
1537
 
1581
- const svgWithViewBox =
1582
- svgUse('icon-star', { viewBox: '0 0 24 24' })
1538
+ const svgWithViewBox = svgUse('icon-star', {
1539
+ viewBox: '0 0 24 24',
1540
+ })
1583
1541
 
1584
- expect(svgWithViewBox.getAttribute('viewBox'))
1585
- .to.equal('0 0 24 24')
1542
+ expect(svgWithViewBox.getAttribute('viewBox')).to.equal(
1543
+ '0 0 24 24'
1544
+ )
1586
1545
 
1587
1546
  const useViewBox = svgWithViewBox.children[0]
1588
1547
 
1589
- expect(useViewBox.getAttributeNS(XLINK_NS, 'href'))
1590
- .to.equal('#icon-star')
1548
+ expect(
1549
+ useViewBox.getAttributeNS(XLINK_NS, 'href')
1550
+ ).to.equal('#icon-star')
1591
1551
 
1592
- const svgWithClass =
1593
- svgUse('icon-menu', { class: 'icon-btn' })
1552
+ const svgWithClass = svgUse('icon-menu', {
1553
+ class: 'icon-btn',
1554
+ })
1594
1555
 
1595
- expect(svgWithClass.getAttribute('class'))
1596
- .to.equal('icon-btn')
1556
+ expect(svgWithClass.getAttribute('class')).to.equal(
1557
+ 'icon-btn'
1558
+ )
1597
1559
 
1598
1560
  expect(svgWithClass.children.length).to.equal(1)
1599
1561
 
1600
1562
  const useClass = svgWithClass.children[0]
1601
1563
 
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' },
1564
+ expect(useClass.getAttributeNS(XLINK_NS, 'href')).to.equal(
1565
+ '#icon-menu'
1608
1566
  )
1609
1567
 
1610
- expect(svgWithMultipleAttrs.getAttribute('width'))
1611
- .to.equal('24')
1568
+ const svgWithMultipleAttrs = svgUse('icon-settings', {
1569
+ width: 24,
1570
+ height: 24,
1571
+ class: 'icon',
1572
+ })
1573
+
1574
+ expect(svgWithMultipleAttrs.getAttribute('width')).to.equal(
1575
+ '24'
1576
+ )
1612
1577
 
1613
- expect(svgWithMultipleAttrs.getAttribute('height'))
1614
- .to.equal('24')
1578
+ expect(
1579
+ svgWithMultipleAttrs.getAttribute('height')
1580
+ ).to.equal('24')
1615
1581
 
1616
- expect(svgWithMultipleAttrs.getAttribute('class'))
1617
- .to.equal('icon')
1582
+ expect(svgWithMultipleAttrs.getAttribute('class')).to.equal(
1583
+ 'icon'
1584
+ )
1618
1585
 
1619
1586
  const useMultiple = svgWithMultipleAttrs.children[0]
1620
1587
 
1621
- expect(useMultiple.getAttributeNS(XLINK_NS, 'href'))
1622
- .to.equal('#icon-settings')
1588
+ expect(
1589
+ useMultiple.getAttributeNS(XLINK_NS, 'href')
1590
+ ).to.equal('#icon-settings')
1623
1591
  ```
1624
1592
 
1625
1593
  ### uuidV1
@@ -1683,17 +1651,19 @@ for (let i = 1; i <= 22136; ++i) {
1683
1651
  }
1684
1652
  }
1685
1653
 
1686
- expect(uuidV1(new Date(), '000123456789abc').split('-')[4])
1687
- .to.equal('123456789abc')
1654
+ expect(
1655
+ uuidV1(new Date(), '000123456789abc').split('-')[4]
1656
+ ).to.equal('123456789abc')
1688
1657
 
1689
- expect(uuidV1(new Date(), '123456789').split('-')[4])
1690
- .to.equal('000123456789')
1658
+ expect(
1659
+ uuidV1(new Date(), '123456789').split('-')[4]
1660
+ ).to.equal('000123456789')
1691
1661
 
1692
1662
  expect(
1693
- uuidV1(new Date(323325000000))
1694
- .startsWith('c1399400-9a71-11bd'),
1695
- )
1696
- .to.be.true
1663
+ uuidV1(new Date(323325000000)).startsWith(
1664
+ 'c1399400-9a71-11bd'
1665
+ )
1666
+ ).to.be.true
1697
1667
  ```
1698
1668
 
1699
1669
  ### vivify
@@ -1749,58 +1719,48 @@ const ref: any = {}
1749
1719
 
1750
1720
  vivify(ref).one.two[3][4]
1751
1721
 
1752
- expect(ref).to.deep.equal(
1753
- {
1754
- one: {
1755
- two: [undefined, undefined, undefined, []],
1756
- },
1722
+ expect(ref).to.deep.equal({
1723
+ one: {
1724
+ two: [undefined, undefined, undefined, []],
1757
1725
  },
1758
- )
1726
+ })
1759
1727
 
1760
1728
  vivify(ref).one.two[3][4] = 5
1761
1729
 
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
- },
1730
+ expect(ref).to.deep.equal({
1731
+ one: {
1732
+ two: [
1733
+ undefined,
1734
+ undefined,
1735
+ undefined,
1736
+ [undefined, undefined, undefined, undefined, 5],
1737
+ ],
1772
1738
  },
1773
- )
1739
+ })
1774
1740
 
1775
1741
  vivify(ref).one.two[3].length = 1
1776
1742
 
1777
- expect(ref).to.deep.equal(
1778
- {
1779
- one: {
1780
- two: [undefined, undefined, undefined, [undefined]],
1781
- },
1743
+ expect(ref).to.deep.equal({
1744
+ one: {
1745
+ two: [undefined, undefined, undefined, [undefined]],
1782
1746
  },
1783
- )
1747
+ })
1784
1748
 
1785
1749
  vivify(ref).one.two[3] = 4
1786
1750
 
1787
- expect(ref).to.deep.equal(
1788
- {
1789
- one: {
1790
- two: [undefined, undefined, undefined, 4],
1791
- },
1751
+ expect(ref).to.deep.equal({
1752
+ one: {
1753
+ two: [undefined, undefined, undefined, 4],
1792
1754
  },
1793
- )
1755
+ })
1794
1756
 
1795
1757
  expect(vivify(ref).one.two.length).to.equal(4)
1796
1758
 
1797
- expect(ref).to.deep.equal(
1798
- {
1799
- one: {
1800
- two: [undefined, undefined, undefined, 4],
1801
- },
1759
+ expect(ref).to.deep.equal({
1760
+ one: {
1761
+ two: [undefined, undefined, undefined, 4],
1802
1762
  },
1803
- )
1763
+ })
1804
1764
 
1805
1765
  vivify(ref).one.two = 3
1806
1766
 
@@ -1814,8 +1774,8 @@ delete vivify(ref).one.two
1814
1774
 
1815
1775
  expect(ref).to.deep.equal({ one: {} })
1816
1776
 
1817
- expect(vivify(ref).one.toString instanceof Function)
1818
- .to.be.true
1777
+ expect(vivify(ref).one.toString instanceof Function).to.be
1778
+ .true
1819
1779
 
1820
1780
  expect(vivify(ref).one.toString.name).to.equal('toString')
1821
1781
 
@@ -1835,14 +1795,17 @@ expect(ref).to.deep.equal({ one: { two: { three: {} } } })
1835
1795
 
1836
1796
  vivify(ref).one.two.three.four = 5
1837
1797
 
1838
- expect(ref)
1839
- .to.deep.equal({ one: { two: { three: { four: 5 } } } })
1798
+ expect(ref).to.deep.equal({
1799
+ one: { two: { three: { four: 5 } } },
1800
+ })
1840
1801
 
1841
- expect(vivify(ref).one.two.three.four.toString.name)
1842
- .to.equal('toString')
1802
+ expect(
1803
+ vivify(ref).one.two.three.four.toString.name
1804
+ ).to.equal('toString')
1843
1805
 
1844
- expect(ref)
1845
- .to.deep.equal({ one: { two: { three: { four: 5 } } } })
1806
+ expect(ref).to.deep.equal({
1807
+ one: { two: { three: { four: 5 } } },
1808
+ })
1846
1809
 
1847
1810
  const u = undefined
1848
1811