@jackens/nnn 2026.4.14 → 2026.8.24
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.
- package/nnn.d.ts +89 -89
- package/nnn.js +75 -76
- package/package.json +1 -1
- package/readme.md +529 -451
package/readme.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# nnn
|
|
2
2
|
|
|
3
|
-
A collection of Jackens’ JavaScript helper utilities (version: `2026.
|
|
3
|
+
A collection of Jackens’ JavaScript helper utilities (version: `2026.8.24`).
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -19,30 +19,29 @@ npm i @jackens/nnn
|
|
|
19
19
|
```js
|
|
20
20
|
import {
|
|
21
21
|
c,
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
omit,
|
|
39
|
-
pick,
|
|
40
|
-
rwd,
|
|
41
|
-
s,
|
|
42
|
-
svgUse,
|
|
43
|
-
uuidV1,
|
|
44
|
-
vivify,
|
|
45
|
-
|
|
22
|
+
createEscape,
|
|
23
|
+
createPluralRules,
|
|
24
|
+
createTokenizer,
|
|
25
|
+
csvParse,
|
|
26
|
+
fixPlTypography,
|
|
27
|
+
h,
|
|
28
|
+
hasOwn,
|
|
29
|
+
isArray,
|
|
30
|
+
isFiniteNumber,
|
|
31
|
+
isInteger,
|
|
32
|
+
isNumber,
|
|
33
|
+
isRecord,
|
|
34
|
+
isString,
|
|
35
|
+
jsOnParse,
|
|
36
|
+
monokai,
|
|
37
|
+
nanolightTs,
|
|
38
|
+
omit,
|
|
39
|
+
pick,
|
|
40
|
+
rwd,
|
|
41
|
+
s,
|
|
42
|
+
svgUse,
|
|
43
|
+
uuidV1,
|
|
44
|
+
vivify,
|
|
46
45
|
} from '@jackens/nnn' // or './node_modules/@jackens/nnn/nnn.js'
|
|
47
46
|
```
|
|
48
47
|
|
|
@@ -62,6 +61,16 @@ vivify,
|
|
|
62
61
|
A minimal CSS-in-JS helper
|
|
63
62
|
that converts a JavaScript object hierarchy
|
|
64
63
|
into a CSS string.
|
|
64
|
+
- [`createEscape`](#createEscape):
|
|
65
|
+
Creates a tag function
|
|
66
|
+
for escaping interpolated values in template literals.
|
|
67
|
+
- [`createPluralRules`](#createPluralRules):
|
|
68
|
+
Creates a function that
|
|
69
|
+
returns the appropriate noun form
|
|
70
|
+
based on a numeric value using `Intl.PluralRules`.
|
|
71
|
+
- [`createTokenizer`](#createTokenizer):
|
|
72
|
+
A helper for building simple tokenizers
|
|
73
|
+
(see also [`nanolightTs`](#nanolightTs)).
|
|
65
74
|
- [`csvParse`](#csvParse):
|
|
66
75
|
Parses a CSV string
|
|
67
76
|
into a two-dimensional array of strings.
|
|
@@ -99,17 +108,7 @@ vivify,
|
|
|
99
108
|
and [`nanolightTs`](#nanolightTs) tokenizer.
|
|
100
109
|
- [`nanolightTs`](#nanolightTs):
|
|
101
110
|
A TypeScript/JavaScript syntax highlighting tokenizer
|
|
102
|
-
built using [`
|
|
103
|
-
- [`newEscape`](#newEscape):
|
|
104
|
-
Creates a tag function
|
|
105
|
-
for escaping interpolated values in template literals.
|
|
106
|
-
- [`newNounForm`](#newNounForm):
|
|
107
|
-
Creates a function that
|
|
108
|
-
returns the appropriate noun form
|
|
109
|
-
based on a numeric value using `Intl.PluralRules`.
|
|
110
|
-
- [`newTokenizer`](#newTokenizer):
|
|
111
|
-
A helper for building simple tokenizers
|
|
112
|
-
(see also [`nanolightTs`](#nanolightTs)).
|
|
111
|
+
built using [`createTokenizer`](#createTokenizer).
|
|
113
112
|
- [`omit`](#omit):
|
|
114
113
|
Creates a new object excluding the specified keys
|
|
115
114
|
from the source object.
|
|
@@ -270,7 +269,8 @@ const actual3 = c({
|
|
|
270
269
|
'@font-face$$1': {
|
|
271
270
|
fontFamily: 'Jackens',
|
|
272
271
|
src$$1: 'url(otf/jackens.otf)',
|
|
273
|
-
src$$2:
|
|
272
|
+
src$$2:
|
|
273
|
+
"url(otf/jackens.otf) format('opentype')," +
|
|
274
274
|
"url(svg/jackens.svg) format('svg')",
|
|
275
275
|
font_weight: 'normal',
|
|
276
276
|
'font-style': 'normal',
|
|
@@ -399,6 +399,294 @@ const expected6 = `
|
|
|
399
399
|
expect(actual6).to.equal(expected6)
|
|
400
400
|
```
|
|
401
401
|
|
|
402
|
+
### createEscape
|
|
403
|
+
|
|
404
|
+
```ts
|
|
405
|
+
const createEscape: (escapeFn: (value: any) => string) => (template: TemplateStringsArray, ...values: unknown[]) => string;
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
Creates a tag function
|
|
409
|
+
for escaping interpolated values in template literals.
|
|
410
|
+
|
|
411
|
+
#### escapeFn
|
|
412
|
+
|
|
413
|
+
A function that takes a value
|
|
414
|
+
and returns its escaped string representation.
|
|
415
|
+
|
|
416
|
+
#### Returns
|
|
417
|
+
|
|
418
|
+
A tag function that escapes interpolated values
|
|
419
|
+
using the provided escape map.
|
|
420
|
+
|
|
421
|
+
#### Usage Examples
|
|
422
|
+
|
|
423
|
+
```ts
|
|
424
|
+
const escapeFn = (value: any): string =>
|
|
425
|
+
isArray(value)
|
|
426
|
+
? value.map(escapeFn).join(', ')
|
|
427
|
+
: value === true || value === false
|
|
428
|
+
? `b'${+value}'`
|
|
429
|
+
: value instanceof Date
|
|
430
|
+
? `'${value
|
|
431
|
+
.toISOString()
|
|
432
|
+
.replace(/^(.+)T(.+)\..*$/, '$1 $2')}'`
|
|
433
|
+
: isFiniteNumber(value)
|
|
434
|
+
? `${value}`
|
|
435
|
+
: isString(value)
|
|
436
|
+
? `'${value.replace(/'/g, "''")}'`
|
|
437
|
+
: 'NULL'
|
|
438
|
+
|
|
439
|
+
const sql = createEscape(escapeFn)
|
|
440
|
+
|
|
441
|
+
const actual = sql`
|
|
442
|
+
SELECT *
|
|
443
|
+
FROM table_name
|
|
444
|
+
WHERE column_name IN (
|
|
445
|
+
${[
|
|
446
|
+
true,
|
|
447
|
+
null,
|
|
448
|
+
undefined,
|
|
449
|
+
NaN,
|
|
450
|
+
Infinity,
|
|
451
|
+
42,
|
|
452
|
+
'42',
|
|
453
|
+
"4'2",
|
|
454
|
+
/42/,
|
|
455
|
+
new Date(323325000000),
|
|
456
|
+
]}
|
|
457
|
+
)`
|
|
458
|
+
|
|
459
|
+
const expected = `
|
|
460
|
+
SELECT *
|
|
461
|
+
FROM table_name
|
|
462
|
+
WHERE column_name IN (
|
|
463
|
+
b'1', NULL, NULL, NULL, NULL, 42, '42', '4''2', NULL, '1980-03-31 04:30:00'
|
|
464
|
+
)`
|
|
465
|
+
|
|
466
|
+
expect(actual).to.equal(expected)
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
### createPluralRules
|
|
470
|
+
|
|
471
|
+
```ts
|
|
472
|
+
const createPluralRules: (locale: string, forms: Partial<Record<Intl.LDMLPluralRule, string>>) => (value: number) => string;
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
Creates a function that
|
|
476
|
+
returns the appropriate noun form
|
|
477
|
+
based on a numeric value using `Intl.PluralRules`.
|
|
478
|
+
|
|
479
|
+
Different languages have different plural rules.
|
|
480
|
+
The `Intl.PluralRules` API provides locale-aware
|
|
481
|
+
plural category selection.
|
|
482
|
+
Possible categories are:
|
|
483
|
+
|
|
484
|
+
- `zero`: for zero items
|
|
485
|
+
(used in some languages like Arabic, Latvian)
|
|
486
|
+
- `one`: for singular (e.g., 1 item)
|
|
487
|
+
- `two`: for dual
|
|
488
|
+
(used in some languages like Arabic, Hebrew)
|
|
489
|
+
- `few`: for small plurals (e.g., 2-4 in Polish)
|
|
490
|
+
- `many`: for larger plurals (e.g., 5-21 in Polish)
|
|
491
|
+
- `other`: fallback category (used by all languages)
|
|
492
|
+
|
|
493
|
+
#### locale
|
|
494
|
+
|
|
495
|
+
A BCP 47 language tag (e.g., `pl`, `en`).
|
|
496
|
+
|
|
497
|
+
#### forms
|
|
498
|
+
|
|
499
|
+
An object mapping plural categories to noun forms.
|
|
500
|
+
Not all categories need to be provided;
|
|
501
|
+
if a category is missing,
|
|
502
|
+
the function falls back to `other`,
|
|
503
|
+
then to an empty string.
|
|
504
|
+
|
|
505
|
+
#### Returns
|
|
506
|
+
|
|
507
|
+
A function that takes a numeric value
|
|
508
|
+
and returns the appropriate noun form.
|
|
509
|
+
|
|
510
|
+
#### Usage Examples
|
|
511
|
+
|
|
512
|
+
```ts
|
|
513
|
+
const auto = createPluralRules('pl', {
|
|
514
|
+
one: 'auto',
|
|
515
|
+
few: 'auta',
|
|
516
|
+
other: 'aut',
|
|
517
|
+
})
|
|
518
|
+
|
|
519
|
+
expect(auto(0)).to.equal('aut')
|
|
520
|
+
expect(auto(1)).to.equal('auto')
|
|
521
|
+
expect(auto(17)).to.equal('aut')
|
|
522
|
+
expect(auto(42)).to.equal('auta')
|
|
523
|
+
|
|
524
|
+
const car = createPluralRules('en', {
|
|
525
|
+
one: 'car',
|
|
526
|
+
other: 'cars',
|
|
527
|
+
})
|
|
528
|
+
|
|
529
|
+
expect(car(0)).to.equal('cars')
|
|
530
|
+
expect(car(1)).to.equal('car')
|
|
531
|
+
expect(car(17)).to.equal('cars')
|
|
532
|
+
expect(car(42)).to.equal('cars')
|
|
533
|
+
|
|
534
|
+
const empty = createPluralRules('en', {})
|
|
535
|
+
|
|
536
|
+
expect(empty(0)).to.equal('')
|
|
537
|
+
expect(empty(1)).to.equal('')
|
|
538
|
+
expect(empty(17)).to.equal('')
|
|
539
|
+
expect(empty(42)).to.equal('')
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
### createTokenizer
|
|
543
|
+
|
|
544
|
+
```ts
|
|
545
|
+
const createTokenizer: <M, T>(decorator: (chunk: string, metadata?: M) => T, ...specs: [M, string | RegExp][]) => (code: string) => T[];
|
|
546
|
+
```
|
|
547
|
+
|
|
548
|
+
A helper for building simple tokenizers
|
|
549
|
+
(see also [`nanolightTs`](#nanolightTs)).
|
|
550
|
+
|
|
551
|
+
#### decorator
|
|
552
|
+
|
|
553
|
+
A function that wraps each matched chunk.
|
|
554
|
+
It receives the matched text (`chunk`)
|
|
555
|
+
and optionally the `metadata`
|
|
556
|
+
associated with the pattern that produced the match.
|
|
557
|
+
For unmatched text between patterns,
|
|
558
|
+
`metadata` is `undefined`.
|
|
559
|
+
|
|
560
|
+
#### specs
|
|
561
|
+
|
|
562
|
+
An array of tuples `[metadata, pattern]` where:
|
|
563
|
+
- `metadata`: arbitrary data (e.g., a CSS class name)
|
|
564
|
+
passed to `decorator` when the pattern matches.
|
|
565
|
+
- `pattern`: a `string` or `RegExp`
|
|
566
|
+
to match against the input.
|
|
567
|
+
|
|
568
|
+
#### Returns
|
|
569
|
+
|
|
570
|
+
A tokenizer function that accepts a code string
|
|
571
|
+
and returns an array of decorated tokens.
|
|
572
|
+
|
|
573
|
+
#### Remarks
|
|
574
|
+
|
|
575
|
+
1. Matches starting at an earlier position
|
|
576
|
+
take precedence.
|
|
577
|
+
2. Among matches at the same position,
|
|
578
|
+
the longer one wins.
|
|
579
|
+
3. Among matches of the same position and length,
|
|
580
|
+
the one defined earlier wins.
|
|
581
|
+
|
|
582
|
+
#### Usage Examples
|
|
583
|
+
|
|
584
|
+
```ts
|
|
585
|
+
const tokenizer1 = createTokenizer(
|
|
586
|
+
(chunk, metadata) => ({ chunk, metadata }),
|
|
587
|
+
['keyword', /\b(if|else|for)\b/],
|
|
588
|
+
['string', /"[^"]*"/]
|
|
589
|
+
)
|
|
590
|
+
|
|
591
|
+
const result1 = tokenizer1('if "hello" else "world"')
|
|
592
|
+
|
|
593
|
+
expect(result1).to.deep.equal([
|
|
594
|
+
{ chunk: 'if', metadata: 'keyword' },
|
|
595
|
+
{ chunk: ' ', metadata: undefined },
|
|
596
|
+
{ chunk: '"hello"', metadata: 'string' },
|
|
597
|
+
{ chunk: ' ', metadata: undefined },
|
|
598
|
+
{ chunk: 'else', metadata: 'keyword' },
|
|
599
|
+
{ chunk: ' ', metadata: undefined },
|
|
600
|
+
{ chunk: '"world"', metadata: 'string' },
|
|
601
|
+
])
|
|
602
|
+
|
|
603
|
+
const tokenizer2 = createTokenizer(
|
|
604
|
+
(chunk, metadata) => `${metadata}:${chunk}`,
|
|
605
|
+
['tag', 'BEGIN'],
|
|
606
|
+
['end', 'END']
|
|
607
|
+
)
|
|
608
|
+
|
|
609
|
+
const result2 = tokenizer2('aBEGINbENDc')
|
|
610
|
+
|
|
611
|
+
expect(result2).to.deep.equal([
|
|
612
|
+
'undefined:a',
|
|
613
|
+
'tag:BEGIN',
|
|
614
|
+
'undefined:b',
|
|
615
|
+
'end:END',
|
|
616
|
+
'undefined:c',
|
|
617
|
+
])
|
|
618
|
+
|
|
619
|
+
const tokenizer3 = createTokenizer(
|
|
620
|
+
chunk => chunk,
|
|
621
|
+
['test', /test/]
|
|
622
|
+
)
|
|
623
|
+
|
|
624
|
+
const result3 = tokenizer3('')
|
|
625
|
+
|
|
626
|
+
expect(result3).to.deep.equal([])
|
|
627
|
+
|
|
628
|
+
const tokenizer4 = createTokenizer(
|
|
629
|
+
(chunk, metadata) => ({ chunk, metadata }),
|
|
630
|
+
['start', /^test/]
|
|
631
|
+
)
|
|
632
|
+
|
|
633
|
+
const result4 = tokenizer4('test here')
|
|
634
|
+
|
|
635
|
+
expect(result4).to.deep.equal([
|
|
636
|
+
{ chunk: 'test', metadata: 'start' },
|
|
637
|
+
{ chunk: ' here', metadata: undefined },
|
|
638
|
+
])
|
|
639
|
+
|
|
640
|
+
const tokenizer5 = createTokenizer(
|
|
641
|
+
(chunk, metadata) => metadata,
|
|
642
|
+
['later', /x/],
|
|
643
|
+
['earlier', /y/]
|
|
644
|
+
)
|
|
645
|
+
|
|
646
|
+
const result5 = tokenizer5('yx')
|
|
647
|
+
|
|
648
|
+
expect(result5).to.deep.equal(['earlier', 'later'])
|
|
649
|
+
|
|
650
|
+
const tokenizer6 = createTokenizer(
|
|
651
|
+
chunk => chunk,
|
|
652
|
+
['short', 'a'],
|
|
653
|
+
['long', 'abc']
|
|
654
|
+
)
|
|
655
|
+
|
|
656
|
+
const result6 = tokenizer6('abc')
|
|
657
|
+
|
|
658
|
+
expect(result6).to.deep.equal(['abc'])
|
|
659
|
+
|
|
660
|
+
const tokenizer7 = createTokenizer(
|
|
661
|
+
chunk => chunk,
|
|
662
|
+
['empty', ''],
|
|
663
|
+
['word', /\w+/]
|
|
664
|
+
)
|
|
665
|
+
|
|
666
|
+
const result7 = tokenizer7('hello')
|
|
667
|
+
|
|
668
|
+
expect(result7).to.deep.equal(['hello'])
|
|
669
|
+
|
|
670
|
+
const tokenizer8 = createTokenizer(
|
|
671
|
+
chunk => chunk,
|
|
672
|
+
['test', /xyz/]
|
|
673
|
+
)
|
|
674
|
+
|
|
675
|
+
const result8 = tokenizer8('abc')
|
|
676
|
+
|
|
677
|
+
expect(result8).to.deep.equal(['abc'])
|
|
678
|
+
|
|
679
|
+
const tokenizer9 = createTokenizer(
|
|
680
|
+
(_chunk, metadata) => metadata,
|
|
681
|
+
['a', 'a'],
|
|
682
|
+
['b', 'b']
|
|
683
|
+
)
|
|
684
|
+
|
|
685
|
+
const result9 = tokenizer9('aabb')
|
|
686
|
+
|
|
687
|
+
expect(result9).to.deep.equal(['a', 'a', 'b', 'b'])
|
|
688
|
+
```
|
|
689
|
+
|
|
402
690
|
### csvParse
|
|
403
691
|
|
|
404
692
|
```ts
|
|
@@ -470,23 +758,25 @@ except those inside `IFRAME`, `NOSCRIPT`, `PRE`,
|
|
|
470
758
|
#### Usage Examples
|
|
471
759
|
|
|
472
760
|
```ts
|
|
473
|
-
const p = h(
|
|
761
|
+
const p = h(
|
|
762
|
+
'p',
|
|
474
763
|
'Pchnąć w tę łódź jeża lub ośm skrzyń fig ' +
|
|
475
|
-
|
|
764
|
+
'(zob. https://pl.wikipedia.org/wiki/Pangram).',
|
|
476
765
|
['br'],
|
|
477
|
-
['b', 'Zażółć gęślą jaźń.']
|
|
766
|
+
['b', 'Zażółć gęślą jaźń.']
|
|
478
767
|
)
|
|
479
768
|
|
|
480
769
|
fixPlTypography(p)
|
|
481
770
|
|
|
482
771
|
expect(p.innerHTML).to.deep.equal(
|
|
483
772
|
'Pchnąć ' +
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
773
|
+
'<span style="white-space:nowrap">w </span>' +
|
|
774
|
+
'tę łódź jeża lub ośm skrzyń fig ' +
|
|
775
|
+
'(zob. https://\u200Bpl.\u200Bwikipedia.\u200Borg/' +
|
|
776
|
+
'\u200Bwiki/\u200BPangram).' +
|
|
777
|
+
'<br>' +
|
|
778
|
+
'<b>Zażółć gęślą jaźń.</b>'
|
|
779
|
+
)
|
|
490
780
|
```
|
|
491
781
|
|
|
492
782
|
### h
|
|
@@ -539,38 +829,46 @@ const i = h('i', 'text')
|
|
|
539
829
|
h(b, i)
|
|
540
830
|
|
|
541
831
|
expect(i.outerHTML).to.equal('<i>text</i>')
|
|
832
|
+
|
|
542
833
|
expect(b.outerHTML).to.equal('<b><i>text</i></b>')
|
|
543
834
|
|
|
544
835
|
h(i, { $className: 'some class' })
|
|
545
836
|
|
|
546
|
-
expect(i.outerHTML)
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
.to.equal('<b><i class="some class">text</i></b>')
|
|
837
|
+
expect(i.outerHTML).to.equal(
|
|
838
|
+
'<i class="some class">text</i>'
|
|
839
|
+
)
|
|
550
840
|
|
|
551
|
-
expect(
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
.to.equal('<span>text</span>')
|
|
841
|
+
expect(b.outerHTML).to.equal(
|
|
842
|
+
'<b><i class="some class">text</i></b>'
|
|
843
|
+
)
|
|
555
844
|
|
|
556
|
-
expect(h('span', '
|
|
557
|
-
|
|
558
|
-
|
|
845
|
+
expect(h('span', 'text').outerHTML).to.equal(
|
|
846
|
+
'<span>text</span>'
|
|
847
|
+
)
|
|
559
848
|
|
|
560
|
-
expect(h('div', { style: 'margin:0;padding:0' }).outerHTML)
|
|
561
|
-
.to.equal('<div style="margin:0;padding:0"></div>')
|
|
562
|
-
expect(h('div', { $style: 'margin:0;padding:0' }).outerHTML)
|
|
563
|
-
.to.equal(
|
|
564
|
-
'<div style="margin: 0px; padding: 0px;"></div>',
|
|
565
|
-
)
|
|
566
849
|
expect(
|
|
567
|
-
h(
|
|
568
|
-
|
|
569
|
-
{ $style: { margin: 0, padding: 0 } },
|
|
570
|
-
).outerHTML,
|
|
571
|
-
).to.equal('<div style="margin: 0px; padding: 0px;"></div>')
|
|
850
|
+
h('span', { $innerText: 'text' }).outerHTML
|
|
851
|
+
).to.equal('<span>text</span>')
|
|
572
852
|
|
|
573
|
-
|
|
853
|
+
expect(h('span', '42').outerHTML).to.equal(
|
|
854
|
+
'<span>42</span>'
|
|
855
|
+
)
|
|
856
|
+
|
|
857
|
+
expect(h('span', 42).outerHTML).to.equal('<span>42</span>')
|
|
858
|
+
|
|
859
|
+
expect(
|
|
860
|
+
h('div', { style: 'margin:0;padding:0' }).outerHTML
|
|
861
|
+
).to.equal('<div style="margin:0;padding:0"></div>')
|
|
862
|
+
|
|
863
|
+
expect(
|
|
864
|
+
h('div', { $style: 'margin:0;padding:0' }).outerHTML
|
|
865
|
+
).to.equal('<div style="margin: 0px; padding: 0px;"></div>')
|
|
866
|
+
|
|
867
|
+
expect(
|
|
868
|
+
h('div', { $style: { margin: 0, padding: 0 } }).outerHTML
|
|
869
|
+
).to.equal('<div style="margin: 0px; padding: 0px;"></div>')
|
|
870
|
+
|
|
871
|
+
const input1 = h('input', { value: 42 })
|
|
574
872
|
const input2 = h('input', { $value: '42' })
|
|
575
873
|
|
|
576
874
|
expect(input1.value).to.equal('42')
|
|
@@ -579,22 +877,26 @@ expect(input2.value).to.equal('42')
|
|
|
579
877
|
expect(input1.outerHTML).to.equal('<input value="42">')
|
|
580
878
|
expect(input2.outerHTML).to.equal('<input>')
|
|
581
879
|
|
|
582
|
-
const checkbox1 = h(
|
|
583
|
-
'
|
|
584
|
-
|
|
585
|
-
)
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
880
|
+
const checkbox1 = h('input', {
|
|
881
|
+
type: 'checkbox',
|
|
882
|
+
checked: true,
|
|
883
|
+
})
|
|
884
|
+
|
|
885
|
+
const checkbox2 = h('input', {
|
|
886
|
+
type: 'checkbox',
|
|
887
|
+
$checked: true,
|
|
888
|
+
})
|
|
590
889
|
|
|
591
890
|
expect(checkbox1.checked).to.be.true
|
|
592
891
|
expect(checkbox2.checked).to.be.true
|
|
593
892
|
|
|
594
|
-
expect(checkbox1.outerHTML)
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
893
|
+
expect(checkbox1.outerHTML).to.equal(
|
|
894
|
+
'<input type="checkbox" checked="">'
|
|
895
|
+
)
|
|
896
|
+
|
|
897
|
+
expect(checkbox2.outerHTML).to.equal(
|
|
898
|
+
'<input type="checkbox">'
|
|
899
|
+
)
|
|
598
900
|
|
|
599
901
|
const div = h('div')
|
|
600
902
|
|
|
@@ -615,15 +917,20 @@ expect(elemWithClass.getAttribute('class')).to.equal('test')
|
|
|
615
917
|
const elemWithText = h('div', 'initial')
|
|
616
918
|
|
|
617
919
|
h(elemWithText, ' more')
|
|
618
|
-
expect(elemWithText.outerHTML)
|
|
619
|
-
.to.equal('<div>initial more</div>')
|
|
620
920
|
|
|
621
|
-
|
|
921
|
+
expect(elemWithText.outerHTML).to.equal(
|
|
922
|
+
'<div>initial more</div>'
|
|
923
|
+
)
|
|
924
|
+
|
|
925
|
+
const elemWithNested = h(
|
|
926
|
+
'div',
|
|
622
927
|
['span', 'hello'],
|
|
623
|
-
['b', 'world']
|
|
928
|
+
['b', 'world']
|
|
929
|
+
)
|
|
624
930
|
|
|
625
|
-
expect(elemWithNested.outerHTML)
|
|
626
|
-
|
|
931
|
+
expect(elemWithNested.outerHTML).to.equal(
|
|
932
|
+
'<div><span>hello</span><b>world</b></div>'
|
|
933
|
+
)
|
|
627
934
|
```
|
|
628
935
|
|
|
629
936
|
### hasOwn
|
|
@@ -708,8 +1015,9 @@ const fakeArray1 = Object.create({ constructor: Array })
|
|
|
708
1015
|
|
|
709
1016
|
expect(isArray(fakeArray1)).to.be.false
|
|
710
1017
|
|
|
711
|
-
const fakeArray2 =
|
|
712
|
-
|
|
1018
|
+
const fakeArray2 = Object.create({
|
|
1019
|
+
[Symbol.toStringTag]: Array.name,
|
|
1020
|
+
})
|
|
713
1021
|
|
|
714
1022
|
expect(isArray(fakeArray2)).to.be.false
|
|
715
1023
|
```
|
|
@@ -735,9 +1043,13 @@ The value to check.
|
|
|
735
1043
|
|
|
736
1044
|
```ts
|
|
737
1045
|
expect(isFiniteNumber(42)).to.be.true
|
|
1046
|
+
|
|
738
1047
|
expect(isFiniteNumber(Number(42))).to.be.true
|
|
1048
|
+
|
|
739
1049
|
expect(isFiniteNumber(new Number(42))).to.be.false
|
|
1050
|
+
|
|
740
1051
|
expect(isFiniteNumber(NaN)).to.be.false
|
|
1052
|
+
|
|
741
1053
|
expect(isFiniteNumber(Infinity)).to.be.false
|
|
742
1054
|
```
|
|
743
1055
|
|
|
@@ -761,12 +1073,18 @@ The value to check.
|
|
|
761
1073
|
|
|
762
1074
|
```ts
|
|
763
1075
|
expect(isInteger(42)).to.be.true
|
|
1076
|
+
|
|
764
1077
|
expect(isInteger(42.00000000000001)).to.be.false
|
|
1078
|
+
|
|
765
1079
|
expect(isInteger(42.000000000000001)).to.be.true
|
|
766
1080
|
// ^because of loss of precision
|
|
1081
|
+
|
|
767
1082
|
expect(isInteger(Number(42))).to.be.true
|
|
1083
|
+
|
|
768
1084
|
expect(isInteger(new Number(42))).to.be.false
|
|
1085
|
+
|
|
769
1086
|
expect(isInteger(NaN)).to.be.false
|
|
1087
|
+
|
|
770
1088
|
expect(isInteger(Infinity)).to.be.false
|
|
771
1089
|
```
|
|
772
1090
|
|
|
@@ -791,9 +1109,13 @@ The value to check.
|
|
|
791
1109
|
|
|
792
1110
|
```ts
|
|
793
1111
|
expect(isNumber(42)).to.be.true
|
|
1112
|
+
|
|
794
1113
|
expect(isNumber(Number(42))).to.be.true
|
|
1114
|
+
|
|
795
1115
|
expect(isNumber(new Number(42))).to.be.false
|
|
1116
|
+
|
|
796
1117
|
expect(isNumber(NaN)).to.be.true
|
|
1118
|
+
|
|
797
1119
|
expect(isNumber(Infinity)).to.be.true
|
|
798
1120
|
```
|
|
799
1121
|
|
|
@@ -818,22 +1140,26 @@ The value to check.
|
|
|
818
1140
|
|
|
819
1141
|
```ts
|
|
820
1142
|
expect(isRecord({})).to.be.true
|
|
1143
|
+
|
|
821
1144
|
expect(isRecord([])).to.be.false
|
|
1145
|
+
|
|
822
1146
|
expect(isRecord(Object.create(null))).to.be.true
|
|
823
1147
|
|
|
824
1148
|
const fakeNumber1 = Object.create({ constructor: Number })
|
|
825
1149
|
|
|
826
1150
|
expect(isRecord(fakeNumber1)).to.be.true
|
|
827
1151
|
|
|
828
|
-
const fakeNumber2 =
|
|
829
|
-
|
|
1152
|
+
const fakeNumber2 = Object.create({
|
|
1153
|
+
[Symbol.toStringTag]: Number.name,
|
|
1154
|
+
})
|
|
830
1155
|
|
|
831
1156
|
expect(isRecord(fakeNumber2)).to.be.true
|
|
832
1157
|
|
|
833
1158
|
expect(isRecord(new Number(42))).to.be.true
|
|
1159
|
+
|
|
834
1160
|
expect(isRecord(new String('42'))).to.be.true
|
|
835
1161
|
|
|
836
|
-
class FooBar {
|
|
1162
|
+
class FooBar {}
|
|
837
1163
|
|
|
838
1164
|
expect(isRecord(new FooBar())).to.be.true
|
|
839
1165
|
```
|
|
@@ -898,7 +1224,9 @@ const handlers = {
|
|
|
898
1224
|
$foo: () => 'bar',
|
|
899
1225
|
}
|
|
900
1226
|
|
|
901
|
-
const actual1 = jsOnParse(
|
|
1227
|
+
const actual1 = jsOnParse(
|
|
1228
|
+
handlers,
|
|
1229
|
+
`[
|
|
902
1230
|
{
|
|
903
1231
|
"$add": [1, 2]
|
|
904
1232
|
},
|
|
@@ -920,7 +1248,8 @@ const actual1 = jsOnParse(handlers, `[
|
|
|
920
1248
|
"one": 1,
|
|
921
1249
|
"two": 2
|
|
922
1250
|
}
|
|
923
|
-
]`
|
|
1251
|
+
]`
|
|
1252
|
+
)
|
|
924
1253
|
|
|
925
1254
|
expect(actual1).to.deep.equal([
|
|
926
1255
|
3,
|
|
@@ -934,7 +1263,7 @@ expect(actual1).to.deep.equal([
|
|
|
934
1263
|
{
|
|
935
1264
|
$foo: [
|
|
936
1265
|
'The parent object does not have ' +
|
|
937
|
-
|
|
1266
|
+
'exactly one property!',
|
|
938
1267
|
],
|
|
939
1268
|
one: 1,
|
|
940
1269
|
two: 2,
|
|
@@ -943,26 +1272,26 @@ expect(actual1).to.deep.equal([
|
|
|
943
1272
|
|
|
944
1273
|
const actual2 = jsOnParse(
|
|
945
1274
|
{ $notFunc: 'handler not being a function' } as any,
|
|
946
|
-
'{"$notFunc": [1, 2]}'
|
|
1275
|
+
'{"$notFunc": [1, 2]}'
|
|
947
1276
|
)
|
|
948
1277
|
|
|
949
1278
|
expect(actual2).to.deep.equal({ $notFunc: [1, 2] })
|
|
950
1279
|
|
|
951
1280
|
const actual3 = jsOnParse(
|
|
952
1281
|
handlers,
|
|
953
|
-
'{"$unknown_handler_key": [1, 2]}'
|
|
1282
|
+
'{"$unknown_handler_key": [1, 2]}'
|
|
954
1283
|
)
|
|
955
1284
|
|
|
956
|
-
expect(actual3)
|
|
957
|
-
|
|
1285
|
+
expect(actual3).to.deep.equal({
|
|
1286
|
+
$unknown_handler_key: [1, 2],
|
|
1287
|
+
})
|
|
958
1288
|
|
|
959
1289
|
const actual4 = jsOnParse(
|
|
960
1290
|
handlers,
|
|
961
|
-
'{"$add": {"not": "array"}}'
|
|
1291
|
+
'{"$add": {"not": "array"}}'
|
|
962
1292
|
)
|
|
963
1293
|
|
|
964
|
-
expect(actual4)
|
|
965
|
-
.to.deep.equal({ $add: { not: 'array' } })
|
|
1294
|
+
expect(actual4).to.deep.equal({ $add: { not: 'array' } })
|
|
966
1295
|
```
|
|
967
1296
|
|
|
968
1297
|
### monokai
|
|
@@ -982,7 +1311,7 @@ const nanolightTs: (code: string) => HArgs1[];
|
|
|
982
1311
|
```
|
|
983
1312
|
|
|
984
1313
|
A TypeScript/JavaScript syntax highlighting tokenizer
|
|
985
|
-
built using [`
|
|
1314
|
+
built using [`createTokenizer`](#createTokenizer).
|
|
986
1315
|
|
|
987
1316
|
#### code
|
|
988
1317
|
|
|
@@ -1003,7 +1332,8 @@ const codeJs =
|
|
|
1003
1332
|
expect(nanolightTs(codeJs)).to.deep.equal([
|
|
1004
1333
|
['span', { class: 'keyword-1' }, 'const'],
|
|
1005
1334
|
' ',
|
|
1006
|
-
[
|
|
1335
|
+
[
|
|
1336
|
+
'span',
|
|
1007
1337
|
{ class: 'identifier-4' },
|
|
1008
1338
|
'answerToLifeTheUniverseAndEverything',
|
|
1009
1339
|
],
|
|
@@ -1026,275 +1356,6 @@ expect(nanolightTs(codeJs)).to.deep.equal([
|
|
|
1026
1356
|
])
|
|
1027
1357
|
```
|
|
1028
1358
|
|
|
1029
|
-
### newEscape
|
|
1030
|
-
|
|
1031
|
-
```ts
|
|
1032
|
-
const newEscape: (escapeFn: (value: any) => string) => (template: TemplateStringsArray, ...values: unknown[]) => string;
|
|
1033
|
-
```
|
|
1034
|
-
|
|
1035
|
-
Creates a tag function
|
|
1036
|
-
for escaping interpolated values in template literals.
|
|
1037
|
-
|
|
1038
|
-
#### escapeFn
|
|
1039
|
-
|
|
1040
|
-
A function that takes a value
|
|
1041
|
-
and returns its escaped string representation.
|
|
1042
|
-
|
|
1043
|
-
#### Returns
|
|
1044
|
-
|
|
1045
|
-
A tag function that escapes interpolated values
|
|
1046
|
-
using the provided escape map.
|
|
1047
|
-
|
|
1048
|
-
#### Usage Examples
|
|
1049
|
-
|
|
1050
|
-
```ts
|
|
1051
|
-
const escapeFn = (value: any): string =>
|
|
1052
|
-
isArray(value)
|
|
1053
|
-
? value.map(escapeFn).join(', ')
|
|
1054
|
-
: value === true || value === false
|
|
1055
|
-
? `b'${+value}'`
|
|
1056
|
-
: value instanceof Date
|
|
1057
|
-
? `'${value.toISOString().replace(/^(.+)T(.+)\..*$/, '$1 $2')}'`
|
|
1058
|
-
: isFiniteNumber(value)
|
|
1059
|
-
? `${value}`
|
|
1060
|
-
: isString(value)
|
|
1061
|
-
? `'${value.replace(/'/g, "''")}'`
|
|
1062
|
-
: 'NULL'
|
|
1063
|
-
|
|
1064
|
-
const sql = newEscape(escapeFn)
|
|
1065
|
-
|
|
1066
|
-
const actual = sql`
|
|
1067
|
-
SELECT *
|
|
1068
|
-
FROM table_name
|
|
1069
|
-
WHERE column_name IN (${[
|
|
1070
|
-
true,
|
|
1071
|
-
null,
|
|
1072
|
-
undefined,
|
|
1073
|
-
NaN,
|
|
1074
|
-
Infinity,
|
|
1075
|
-
42,
|
|
1076
|
-
'42',
|
|
1077
|
-
"4'2",
|
|
1078
|
-
/42/,
|
|
1079
|
-
new Date(323325000000),
|
|
1080
|
-
]})`
|
|
1081
|
-
|
|
1082
|
-
const expected = `
|
|
1083
|
-
SELECT *
|
|
1084
|
-
FROM table_name
|
|
1085
|
-
WHERE column_name IN (b'1', NULL, NULL, NULL, NULL, 42, '42', '4''2', NULL, '1980-03-31 04:30:00')`
|
|
1086
|
-
|
|
1087
|
-
expect(actual).to.equal(expected)
|
|
1088
|
-
```
|
|
1089
|
-
|
|
1090
|
-
### newNounForm
|
|
1091
|
-
|
|
1092
|
-
```ts
|
|
1093
|
-
const newNounForm: (locale: string, forms: Partial<Record<Intl.LDMLPluralRule, string>>) => (value: number) => string;
|
|
1094
|
-
```
|
|
1095
|
-
|
|
1096
|
-
Creates a function that
|
|
1097
|
-
returns the appropriate noun form
|
|
1098
|
-
based on a numeric value using `Intl.PluralRules`.
|
|
1099
|
-
|
|
1100
|
-
Different languages have different plural rules.
|
|
1101
|
-
The `Intl.PluralRules` API provides locale-aware
|
|
1102
|
-
plural category selection.
|
|
1103
|
-
Possible categories are:
|
|
1104
|
-
|
|
1105
|
-
- `zero`: for zero items
|
|
1106
|
-
(used in some languages like Arabic, Latvian)
|
|
1107
|
-
- `one`: for singular (e.g., 1 item)
|
|
1108
|
-
- `two`: for dual
|
|
1109
|
-
(used in some languages like Arabic, Hebrew)
|
|
1110
|
-
- `few`: for small plurals (e.g., 2-4 in Polish)
|
|
1111
|
-
- `many`: for larger plurals (e.g., 5-21 in Polish)
|
|
1112
|
-
- `other`: fallback category (used by all languages)
|
|
1113
|
-
|
|
1114
|
-
#### locale
|
|
1115
|
-
|
|
1116
|
-
A BCP 47 language tag (e.g., `pl`, `en`).
|
|
1117
|
-
|
|
1118
|
-
#### forms
|
|
1119
|
-
|
|
1120
|
-
An object mapping plural categories to noun forms.
|
|
1121
|
-
Not all categories need to be provided;
|
|
1122
|
-
if a category is missing,
|
|
1123
|
-
the function falls back to `other`,
|
|
1124
|
-
then to an empty string.
|
|
1125
|
-
|
|
1126
|
-
#### Returns
|
|
1127
|
-
|
|
1128
|
-
A function that takes a numeric value
|
|
1129
|
-
and returns the appropriate noun form.
|
|
1130
|
-
|
|
1131
|
-
#### Usage Examples
|
|
1132
|
-
|
|
1133
|
-
```ts
|
|
1134
|
-
const auto = newNounForm(
|
|
1135
|
-
'pl',
|
|
1136
|
-
{ one: 'auto', few: 'auta', other: 'aut' },
|
|
1137
|
-
)
|
|
1138
|
-
|
|
1139
|
-
expect(auto(0)).to.equal('aut')
|
|
1140
|
-
expect(auto(1)).to.equal('auto')
|
|
1141
|
-
expect(auto(17)).to.equal('aut')
|
|
1142
|
-
expect(auto(42)).to.equal('auta')
|
|
1143
|
-
|
|
1144
|
-
const car = newNounForm('en', { one: 'car', other: 'cars' })
|
|
1145
|
-
|
|
1146
|
-
expect(car(0)).to.equal('cars')
|
|
1147
|
-
expect(car(1)).to.equal('car')
|
|
1148
|
-
expect(car(17)).to.equal('cars')
|
|
1149
|
-
expect(car(42)).to.equal('cars')
|
|
1150
|
-
|
|
1151
|
-
const empty = newNounForm('en', {})
|
|
1152
|
-
|
|
1153
|
-
expect(empty(0)).to.equal('')
|
|
1154
|
-
expect(empty(1)).to.equal('')
|
|
1155
|
-
expect(empty(17)).to.equal('')
|
|
1156
|
-
expect(empty(42)).to.equal('')
|
|
1157
|
-
```
|
|
1158
|
-
|
|
1159
|
-
### newTokenizer
|
|
1160
|
-
|
|
1161
|
-
```ts
|
|
1162
|
-
const newTokenizer: <M, T>(decorator: (chunk: string, metadata?: M) => T, ...specs: [M, string | RegExp][]) => (code: string) => T[];
|
|
1163
|
-
```
|
|
1164
|
-
|
|
1165
|
-
A helper for building simple tokenizers
|
|
1166
|
-
(see also [`nanolightTs`](#nanolightTs)).
|
|
1167
|
-
|
|
1168
|
-
#### decorator
|
|
1169
|
-
|
|
1170
|
-
A function that wraps each matched chunk.
|
|
1171
|
-
It receives the matched text (`chunk`)
|
|
1172
|
-
and optionally the `metadata`
|
|
1173
|
-
associated with the pattern that produced the match.
|
|
1174
|
-
For unmatched text between patterns,
|
|
1175
|
-
`metadata` is `undefined`.
|
|
1176
|
-
|
|
1177
|
-
#### specs
|
|
1178
|
-
|
|
1179
|
-
An array of tuples `[metadata, pattern]` where:
|
|
1180
|
-
- `metadata`: arbitrary data (e.g., a CSS class name)
|
|
1181
|
-
passed to `decorator` when the pattern matches.
|
|
1182
|
-
- `pattern`: a `string` or `RegExp`
|
|
1183
|
-
to match against the input.
|
|
1184
|
-
|
|
1185
|
-
#### Returns
|
|
1186
|
-
|
|
1187
|
-
A tokenizer function that accepts a code string
|
|
1188
|
-
and returns an array of decorated tokens.
|
|
1189
|
-
|
|
1190
|
-
#### Remarks
|
|
1191
|
-
|
|
1192
|
-
1. Matches starting at an earlier position
|
|
1193
|
-
take precedence.
|
|
1194
|
-
2. Among matches at the same position,
|
|
1195
|
-
the longer one wins.
|
|
1196
|
-
3. Among matches of the same position and length,
|
|
1197
|
-
the one defined earlier wins.
|
|
1198
|
-
|
|
1199
|
-
#### Usage Examples
|
|
1200
|
-
|
|
1201
|
-
```ts
|
|
1202
|
-
const tokenizer1 = newTokenizer(
|
|
1203
|
-
(chunk, metadata) => ({ chunk, metadata }),
|
|
1204
|
-
['keyword', /\b(if|else|for)\b/],
|
|
1205
|
-
['string', /"[^"]*"/],
|
|
1206
|
-
)
|
|
1207
|
-
const result1 = tokenizer1('if "hello" else "world"')
|
|
1208
|
-
|
|
1209
|
-
expect(result1).to.deep.equal([
|
|
1210
|
-
{ chunk: 'if', metadata: 'keyword' },
|
|
1211
|
-
{ chunk: ' ', metadata: undefined },
|
|
1212
|
-
{ chunk: '"hello"', metadata: 'string' },
|
|
1213
|
-
{ chunk: ' ', metadata: undefined },
|
|
1214
|
-
{ chunk: 'else', metadata: 'keyword' },
|
|
1215
|
-
{ chunk: ' ', metadata: undefined },
|
|
1216
|
-
{ chunk: '"world"', metadata: 'string' },
|
|
1217
|
-
])
|
|
1218
|
-
|
|
1219
|
-
const tokenizer2 = newTokenizer(
|
|
1220
|
-
(chunk, metadata) => `${metadata}:${chunk}`,
|
|
1221
|
-
['tag', 'BEGIN'],
|
|
1222
|
-
['end', 'END'],
|
|
1223
|
-
)
|
|
1224
|
-
const result2 = tokenizer2('aBEGINbENDc')
|
|
1225
|
-
|
|
1226
|
-
expect(result2).to.deep.equal([
|
|
1227
|
-
'undefined:a',
|
|
1228
|
-
'tag:BEGIN',
|
|
1229
|
-
'undefined:b',
|
|
1230
|
-
'end:END',
|
|
1231
|
-
'undefined:c',
|
|
1232
|
-
])
|
|
1233
|
-
|
|
1234
|
-
const tokenizer3 = newTokenizer(
|
|
1235
|
-
(chunk) => chunk,
|
|
1236
|
-
['test', /test/],
|
|
1237
|
-
)
|
|
1238
|
-
const result3 = tokenizer3('')
|
|
1239
|
-
|
|
1240
|
-
expect(result3).to.deep.equal([])
|
|
1241
|
-
|
|
1242
|
-
const tokenizer4 = newTokenizer(
|
|
1243
|
-
(chunk, metadata) => ({ chunk, metadata }),
|
|
1244
|
-
['start', /^test/],
|
|
1245
|
-
)
|
|
1246
|
-
const result4 = tokenizer4('test here')
|
|
1247
|
-
|
|
1248
|
-
expect(result4).to.deep.equal([
|
|
1249
|
-
{ chunk: 'test', metadata: 'start' },
|
|
1250
|
-
{ chunk: ' here', metadata: undefined },
|
|
1251
|
-
])
|
|
1252
|
-
|
|
1253
|
-
const tokenizer5 = newTokenizer(
|
|
1254
|
-
(chunk, metadata) => metadata,
|
|
1255
|
-
['later', /x/],
|
|
1256
|
-
['earlier', /y/],
|
|
1257
|
-
)
|
|
1258
|
-
const result5 = tokenizer5('yx')
|
|
1259
|
-
|
|
1260
|
-
expect(result5).to.deep.equal(['earlier', 'later'])
|
|
1261
|
-
|
|
1262
|
-
const tokenizer6 = newTokenizer(
|
|
1263
|
-
(chunk) => chunk,
|
|
1264
|
-
['short', 'a'],
|
|
1265
|
-
['long', 'abc'],
|
|
1266
|
-
)
|
|
1267
|
-
const result6 = tokenizer6('abc')
|
|
1268
|
-
|
|
1269
|
-
expect(result6).to.deep.equal(['abc'])
|
|
1270
|
-
|
|
1271
|
-
const tokenizer7 = newTokenizer(
|
|
1272
|
-
(chunk) => chunk,
|
|
1273
|
-
['empty', ''],
|
|
1274
|
-
['word', /\w+/],
|
|
1275
|
-
)
|
|
1276
|
-
const result7 = tokenizer7('hello')
|
|
1277
|
-
|
|
1278
|
-
expect(result7).to.deep.equal(['hello'])
|
|
1279
|
-
|
|
1280
|
-
const tokenizer8 = newTokenizer(
|
|
1281
|
-
(chunk) => chunk,
|
|
1282
|
-
['test', /xyz/],
|
|
1283
|
-
)
|
|
1284
|
-
const result8 = tokenizer8('abc')
|
|
1285
|
-
|
|
1286
|
-
expect(result8).to.deep.equal(['abc'])
|
|
1287
|
-
|
|
1288
|
-
const tokenizer9 = newTokenizer(
|
|
1289
|
-
(_chunk, metadata) => metadata,
|
|
1290
|
-
['a', 'a'],
|
|
1291
|
-
['b', 'b'],
|
|
1292
|
-
)
|
|
1293
|
-
const result9 = tokenizer9('aabb')
|
|
1294
|
-
|
|
1295
|
-
expect(result9).to.deep.equal(['a', 'a', 'b', 'b'])
|
|
1296
|
-
```
|
|
1297
|
-
|
|
1298
1359
|
### omit
|
|
1299
1360
|
|
|
1300
1361
|
```ts
|
|
@@ -1358,9 +1419,10 @@ A new object with only the specified keys.
|
|
|
1358
1419
|
```ts
|
|
1359
1420
|
const ref = { a: 42, b: '42', c: 17 }
|
|
1360
1421
|
|
|
1361
|
-
expect(pick(ref, ['a', 'b'])).to.deep.equal(
|
|
1362
|
-
|
|
1363
|
-
|
|
1422
|
+
expect(pick(ref, ['a', 'b'])).to.deep.equal({
|
|
1423
|
+
a: 42,
|
|
1424
|
+
b: '42',
|
|
1425
|
+
})
|
|
1364
1426
|
```
|
|
1365
1427
|
|
|
1366
1428
|
### rwd
|
|
@@ -1415,36 +1477,34 @@ const style: CRoot = {
|
|
|
1415
1477
|
|
|
1416
1478
|
rwd(style, '.r6', 200, 50, [6], [3], [1, 1, 2])
|
|
1417
1479
|
|
|
1418
|
-
expect(style).to.deep.equal(
|
|
1419
|
-
{
|
|
1420
|
-
|
|
1421
|
-
|
|
1480
|
+
expect(style).to.deep.equal({
|
|
1481
|
+
body: {
|
|
1482
|
+
margin: 0,
|
|
1483
|
+
},
|
|
1484
|
+
'.r6': {
|
|
1485
|
+
border: 'solid red 1px',
|
|
1486
|
+
'.no-border': {
|
|
1487
|
+
border: 'none',
|
|
1422
1488
|
},
|
|
1489
|
+
boxSizing: 'border-box',
|
|
1490
|
+
display: 'block',
|
|
1491
|
+
float: 'left',
|
|
1492
|
+
width: '100%',
|
|
1493
|
+
height: '100px',
|
|
1494
|
+
},
|
|
1495
|
+
'@media(min-width:600px)': {
|
|
1423
1496
|
'.r6': {
|
|
1424
|
-
|
|
1425
|
-
'
|
|
1426
|
-
border: 'none',
|
|
1427
|
-
},
|
|
1428
|
-
boxSizing: 'border-box',
|
|
1429
|
-
display: 'block',
|
|
1430
|
-
float: 'left',
|
|
1431
|
-
width: '100%',
|
|
1432
|
-
height: '100px',
|
|
1433
|
-
},
|
|
1434
|
-
'@media(min-width:600px)': {
|
|
1435
|
-
'.r6': {
|
|
1436
|
-
width: 'calc(100% / 3)',
|
|
1437
|
-
height: '50px',
|
|
1438
|
-
},
|
|
1497
|
+
width: 'calc(100% / 3)',
|
|
1498
|
+
height: '50px',
|
|
1439
1499
|
},
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1500
|
+
},
|
|
1501
|
+
'@media(min-width:1200px)': {
|
|
1502
|
+
'.r6': {
|
|
1503
|
+
width: 'calc(50% / 3)',
|
|
1504
|
+
height: '50px',
|
|
1445
1505
|
},
|
|
1446
1506
|
},
|
|
1447
|
-
)
|
|
1507
|
+
})
|
|
1448
1508
|
```
|
|
1449
1509
|
|
|
1450
1510
|
### s
|
|
@@ -1498,18 +1558,19 @@ const svg2 = s('svg', { 'xlink:href': false })
|
|
|
1498
1558
|
|
|
1499
1559
|
expect(svg2.getAttributeNS(XLINK_NS, 'href')).to.be.null
|
|
1500
1560
|
|
|
1501
|
-
const svg3 = s(
|
|
1502
|
-
'
|
|
1503
|
-
|
|
1504
|
-
)
|
|
1561
|
+
const svg3 = s('svg', {
|
|
1562
|
+
'xlink:href': 'http://example.com',
|
|
1563
|
+
})
|
|
1505
1564
|
|
|
1506
|
-
expect(svg3.getAttributeNS(XLINK_NS, 'href'))
|
|
1507
|
-
|
|
1565
|
+
expect(svg3.getAttributeNS(XLINK_NS, 'href')).to.equal(
|
|
1566
|
+
'http://example.com'
|
|
1567
|
+
)
|
|
1508
1568
|
|
|
1509
1569
|
const svg4 = s('svg', { 'xlink:title': 42 })
|
|
1510
1570
|
|
|
1511
|
-
expect(svg4.getAttributeNS(XLINK_NS, 'title'))
|
|
1512
|
-
|
|
1571
|
+
expect(svg4.getAttributeNS(XLINK_NS, 'title')).to.equal(
|
|
1572
|
+
'42'
|
|
1573
|
+
)
|
|
1513
1574
|
```
|
|
1514
1575
|
|
|
1515
1576
|
### svgUse
|
|
@@ -1548,51 +1609,63 @@ expect(svg.children.length).to.equal(1)
|
|
|
1548
1609
|
|
|
1549
1610
|
const useElement = svg.children[0]
|
|
1550
1611
|
|
|
1551
|
-
expect(
|
|
1552
|
-
.
|
|
1612
|
+
expect(
|
|
1613
|
+
useElement.getAttributeNS(XLINK_NS, 'href')
|
|
1614
|
+
).to.equal('#icon-home')
|
|
1553
1615
|
|
|
1554
|
-
const svgWithViewBox =
|
|
1555
|
-
|
|
1616
|
+
const svgWithViewBox = svgUse('icon-star', {
|
|
1617
|
+
viewBox: '0 0 24 24',
|
|
1618
|
+
})
|
|
1556
1619
|
|
|
1557
|
-
expect(svgWithViewBox.getAttribute('viewBox'))
|
|
1558
|
-
|
|
1620
|
+
expect(svgWithViewBox.getAttribute('viewBox')).to.equal(
|
|
1621
|
+
'0 0 24 24'
|
|
1622
|
+
)
|
|
1559
1623
|
|
|
1560
1624
|
const useViewBox = svgWithViewBox.children[0]
|
|
1561
1625
|
|
|
1562
|
-
expect(
|
|
1563
|
-
.
|
|
1626
|
+
expect(
|
|
1627
|
+
useViewBox.getAttributeNS(XLINK_NS, 'href')
|
|
1628
|
+
).to.equal('#icon-star')
|
|
1564
1629
|
|
|
1565
|
-
const svgWithClass =
|
|
1566
|
-
|
|
1630
|
+
const svgWithClass = svgUse('icon-menu', {
|
|
1631
|
+
class: 'icon-btn',
|
|
1632
|
+
})
|
|
1567
1633
|
|
|
1568
|
-
expect(svgWithClass.getAttribute('class'))
|
|
1569
|
-
|
|
1634
|
+
expect(svgWithClass.getAttribute('class')).to.equal(
|
|
1635
|
+
'icon-btn'
|
|
1636
|
+
)
|
|
1570
1637
|
|
|
1571
1638
|
expect(svgWithClass.children.length).to.equal(1)
|
|
1572
1639
|
|
|
1573
1640
|
const useClass = svgWithClass.children[0]
|
|
1574
1641
|
|
|
1575
|
-
expect(useClass.getAttributeNS(XLINK_NS, 'href'))
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
const svgWithMultipleAttrs = svgUse(
|
|
1579
|
-
'icon-settings',
|
|
1580
|
-
{ width: 24, height: 24, class: 'icon' },
|
|
1642
|
+
expect(useClass.getAttributeNS(XLINK_NS, 'href')).to.equal(
|
|
1643
|
+
'#icon-menu'
|
|
1581
1644
|
)
|
|
1582
1645
|
|
|
1583
|
-
|
|
1584
|
-
|
|
1646
|
+
const svgWithMultipleAttrs = svgUse('icon-settings', {
|
|
1647
|
+
width: 24,
|
|
1648
|
+
height: 24,
|
|
1649
|
+
class: 'icon',
|
|
1650
|
+
})
|
|
1651
|
+
|
|
1652
|
+
expect(svgWithMultipleAttrs.getAttribute('width')).to.equal(
|
|
1653
|
+
'24'
|
|
1654
|
+
)
|
|
1585
1655
|
|
|
1586
|
-
expect(
|
|
1587
|
-
.
|
|
1656
|
+
expect(
|
|
1657
|
+
svgWithMultipleAttrs.getAttribute('height')
|
|
1658
|
+
).to.equal('24')
|
|
1588
1659
|
|
|
1589
|
-
expect(svgWithMultipleAttrs.getAttribute('class'))
|
|
1590
|
-
|
|
1660
|
+
expect(svgWithMultipleAttrs.getAttribute('class')).to.equal(
|
|
1661
|
+
'icon'
|
|
1662
|
+
)
|
|
1591
1663
|
|
|
1592
1664
|
const useMultiple = svgWithMultipleAttrs.children[0]
|
|
1593
1665
|
|
|
1594
|
-
expect(
|
|
1595
|
-
.
|
|
1666
|
+
expect(
|
|
1667
|
+
useMultiple.getAttributeNS(XLINK_NS, 'href')
|
|
1668
|
+
).to.equal('#icon-settings')
|
|
1596
1669
|
```
|
|
1597
1670
|
|
|
1598
1671
|
### uuidV1
|
|
@@ -1656,17 +1729,19 @@ for (let i = 1; i <= 22136; ++i) {
|
|
|
1656
1729
|
}
|
|
1657
1730
|
}
|
|
1658
1731
|
|
|
1659
|
-
expect(
|
|
1660
|
-
.
|
|
1732
|
+
expect(
|
|
1733
|
+
uuidV1(new Date(), '000123456789abc').split('-')[4]
|
|
1734
|
+
).to.equal('123456789abc')
|
|
1661
1735
|
|
|
1662
|
-
expect(
|
|
1663
|
-
.
|
|
1736
|
+
expect(
|
|
1737
|
+
uuidV1(new Date(), '123456789').split('-')[4]
|
|
1738
|
+
).to.equal('000123456789')
|
|
1664
1739
|
|
|
1665
1740
|
expect(
|
|
1666
|
-
uuidV1(new Date(323325000000))
|
|
1667
|
-
|
|
1668
|
-
)
|
|
1669
|
-
|
|
1741
|
+
uuidV1(new Date(323325000000)).startsWith(
|
|
1742
|
+
'c1399400-9a71-11bd'
|
|
1743
|
+
)
|
|
1744
|
+
).to.be.true
|
|
1670
1745
|
```
|
|
1671
1746
|
|
|
1672
1747
|
### vivify
|
|
@@ -1777,8 +1852,8 @@ delete vivify(ref).one.two
|
|
|
1777
1852
|
|
|
1778
1853
|
expect(ref).to.deep.equal({ one: {} })
|
|
1779
1854
|
|
|
1780
|
-
expect(vivify(ref).one.toString instanceof Function)
|
|
1781
|
-
.
|
|
1855
|
+
expect(vivify(ref).one.toString instanceof Function).to.be
|
|
1856
|
+
.true
|
|
1782
1857
|
|
|
1783
1858
|
expect(vivify(ref).one.toString.name).to.equal('toString')
|
|
1784
1859
|
|
|
@@ -1798,14 +1873,17 @@ expect(ref).to.deep.equal({ one: { two: { three: {} } } })
|
|
|
1798
1873
|
|
|
1799
1874
|
vivify(ref).one.two.three.four = 5
|
|
1800
1875
|
|
|
1801
|
-
expect(ref)
|
|
1802
|
-
|
|
1876
|
+
expect(ref).to.deep.equal({
|
|
1877
|
+
one: { two: { three: { four: 5 } } },
|
|
1878
|
+
})
|
|
1803
1879
|
|
|
1804
|
-
expect(
|
|
1805
|
-
.
|
|
1880
|
+
expect(
|
|
1881
|
+
vivify(ref).one.two.three.four.toString.name
|
|
1882
|
+
).to.equal('toString')
|
|
1806
1883
|
|
|
1807
|
-
expect(ref)
|
|
1808
|
-
|
|
1884
|
+
expect(ref).to.deep.equal({
|
|
1885
|
+
one: { two: { three: { four: 5 } } },
|
|
1886
|
+
})
|
|
1809
1887
|
|
|
1810
1888
|
const u = undefined
|
|
1811
1889
|
|