@jackens/nnn 2026.4.15 → 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 +828 -787
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,6 +19,9 @@ npm i @jackens/nnn
|
|
|
19
19
|
```js
|
|
20
20
|
import {
|
|
21
21
|
c,
|
|
22
|
+
createEscape,
|
|
23
|
+
createPluralRules,
|
|
24
|
+
createTokenizer,
|
|
22
25
|
csvParse,
|
|
23
26
|
fixPlTypography,
|
|
24
27
|
h,
|
|
@@ -32,9 +35,6 @@ import {
|
|
|
32
35
|
jsOnParse,
|
|
33
36
|
monokai,
|
|
34
37
|
nanolightTs,
|
|
35
|
-
newEscape,
|
|
36
|
-
newNounForm,
|
|
37
|
-
newTokenizer,
|
|
38
38
|
omit,
|
|
39
39
|
pick,
|
|
40
40
|
rwd,
|
|
@@ -61,6 +61,16 @@ import {
|
|
|
61
61
|
A minimal CSS-in-JS helper
|
|
62
62
|
that converts a JavaScript object hierarchy
|
|
63
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)).
|
|
64
74
|
- [`csvParse`](#csvParse):
|
|
65
75
|
Parses a CSV string
|
|
66
76
|
into a two-dimensional array of strings.
|
|
@@ -98,17 +108,7 @@ import {
|
|
|
98
108
|
and [`nanolightTs`](#nanolightTs) tokenizer.
|
|
99
109
|
- [`nanolightTs`](#nanolightTs):
|
|
100
110
|
A TypeScript/JavaScript syntax highlighting tokenizer
|
|
101
|
-
built using [`
|
|
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)).
|
|
111
|
+
built using [`createTokenizer`](#createTokenizer).
|
|
112
112
|
- [`omit`](#omit):
|
|
113
113
|
Creates a new object excluding the specified keys
|
|
114
114
|
from the source object.
|
|
@@ -215,16 +215,14 @@ A CSS string representing the compiled rules.
|
|
|
215
215
|
#### Usage Examples
|
|
216
216
|
|
|
217
217
|
```ts
|
|
218
|
-
const actual1 = c(
|
|
219
|
-
{
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
padding: 1,
|
|
225
|
-
},
|
|
218
|
+
const actual1 = c({
|
|
219
|
+
a: {
|
|
220
|
+
color: 'red',
|
|
221
|
+
margin: 1,
|
|
222
|
+
'.c': { margin: 2, padding: 2 },
|
|
223
|
+
padding: 1,
|
|
226
224
|
},
|
|
227
|
-
)
|
|
225
|
+
})
|
|
228
226
|
|
|
229
227
|
const expected1 = `
|
|
230
228
|
a{
|
|
@@ -241,18 +239,16 @@ a{
|
|
|
241
239
|
|
|
242
240
|
expect(actual1).to.equal(expected1)
|
|
243
241
|
|
|
244
|
-
const actual2 = c(
|
|
245
|
-
{
|
|
246
|
-
|
|
247
|
-
'
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
padding: 1,
|
|
252
|
-
},
|
|
242
|
+
const actual2 = c({
|
|
243
|
+
a: {
|
|
244
|
+
'.b': {
|
|
245
|
+
color: 'red',
|
|
246
|
+
margin: 1,
|
|
247
|
+
'.c': { margin: 2, padding: 2 },
|
|
248
|
+
padding: 1,
|
|
253
249
|
},
|
|
254
250
|
},
|
|
255
|
-
)
|
|
251
|
+
})
|
|
256
252
|
|
|
257
253
|
const expected2 = `
|
|
258
254
|
a.b{
|
|
@@ -269,36 +265,35 @@ a.b{
|
|
|
269
265
|
|
|
270
266
|
expect(actual2).to.equal(expected2)
|
|
271
267
|
|
|
272
|
-
const actual3 = c(
|
|
273
|
-
{
|
|
274
|
-
'
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
268
|
+
const actual3 = c({
|
|
269
|
+
'@font-face$$1': {
|
|
270
|
+
fontFamily: 'Jackens',
|
|
271
|
+
src$$1: 'url(otf/jackens.otf)',
|
|
272
|
+
src$$2:
|
|
273
|
+
"url(otf/jackens.otf) format('opentype')," +
|
|
278
274
|
"url(svg/jackens.svg) format('svg')",
|
|
279
|
-
|
|
280
|
-
|
|
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
|
-
},
|
|
275
|
+
font_weight: 'normal',
|
|
276
|
+
'font-style': 'normal',
|
|
300
277
|
},
|
|
301
|
-
|
|
278
|
+
'@font-face$$2': {
|
|
279
|
+
font_family: 'C64',
|
|
280
|
+
src: 'url(fonts/C64_Pro_Mono-STYLE.woff)',
|
|
281
|
+
},
|
|
282
|
+
'@keyframes spin': {
|
|
283
|
+
'0%': { transform: 'rotate(0deg)' },
|
|
284
|
+
'100%': { transform: 'rotate(360deg)' },
|
|
285
|
+
},
|
|
286
|
+
div: {
|
|
287
|
+
border: 'solid red 1px',
|
|
288
|
+
'.c1': { 'background-color': '#000' },
|
|
289
|
+
' .c1': { background_color: 'black' },
|
|
290
|
+
'.c2': { backgroundColor: 'rgb(0,0,0)' },
|
|
291
|
+
},
|
|
292
|
+
'@media(min-width:200px)': {
|
|
293
|
+
div: { margin: 0, padding: 0 },
|
|
294
|
+
span: { color: '#000' },
|
|
295
|
+
},
|
|
296
|
+
})
|
|
302
297
|
|
|
303
298
|
const expected3 = `
|
|
304
299
|
@font-face{
|
|
@@ -344,18 +339,16 @@ div.c2{
|
|
|
344
339
|
|
|
345
340
|
expect(actual3).to.equal(expected3)
|
|
346
341
|
|
|
347
|
-
const actual4 = c(
|
|
348
|
-
{
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
margin: 2,
|
|
354
|
-
},
|
|
342
|
+
const actual4 = c({
|
|
343
|
+
a: {
|
|
344
|
+
'.b,.c': {
|
|
345
|
+
margin: 1,
|
|
346
|
+
'.d': {
|
|
347
|
+
margin: 2,
|
|
355
348
|
},
|
|
356
349
|
},
|
|
357
350
|
},
|
|
358
|
-
)
|
|
351
|
+
})
|
|
359
352
|
|
|
360
353
|
const expected4 = `
|
|
361
354
|
a.b,a.c{
|
|
@@ -367,16 +360,14 @@ a.b.d,a.c.d{
|
|
|
367
360
|
|
|
368
361
|
expect(actual4).to.equal(expected4)
|
|
369
362
|
|
|
370
|
-
const actual5 = c(
|
|
371
|
-
{
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
margin: 2,
|
|
376
|
-
},
|
|
363
|
+
const actual5 = c({
|
|
364
|
+
'.b,.c': {
|
|
365
|
+
margin: 1,
|
|
366
|
+
'.d': {
|
|
367
|
+
margin: 2,
|
|
377
368
|
},
|
|
378
369
|
},
|
|
379
|
-
)
|
|
370
|
+
})
|
|
380
371
|
|
|
381
372
|
const expected5 = `
|
|
382
373
|
.b,.c{
|
|
@@ -388,16 +379,14 @@ const expected5 = `
|
|
|
388
379
|
|
|
389
380
|
expect(actual5).to.equal(expected5)
|
|
390
381
|
|
|
391
|
-
const actual6 = c(
|
|
392
|
-
{
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
margin: 2,
|
|
397
|
-
},
|
|
382
|
+
const actual6 = c({
|
|
383
|
+
'.a,.b': {
|
|
384
|
+
margin: 1,
|
|
385
|
+
'.c,.d': {
|
|
386
|
+
margin: 2,
|
|
398
387
|
},
|
|
399
388
|
},
|
|
400
|
-
)
|
|
389
|
+
})
|
|
401
390
|
|
|
402
391
|
const expected6 = `
|
|
403
392
|
.a,.b{
|
|
@@ -410,204 +399,504 @@ const expected6 = `
|
|
|
410
399
|
expect(actual6).to.equal(expected6)
|
|
411
400
|
```
|
|
412
401
|
|
|
413
|
-
###
|
|
402
|
+
### createEscape
|
|
414
403
|
|
|
415
404
|
```ts
|
|
416
|
-
const
|
|
405
|
+
const createEscape: (escapeFn: (value: any) => string) => (template: TemplateStringsArray, ...values: unknown[]) => string;
|
|
417
406
|
```
|
|
418
407
|
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
Supports quoted fields with escaped double quotes (`""`).
|
|
423
|
-
Carriage returns are normalized.
|
|
424
|
-
|
|
425
|
-
#### csv
|
|
426
|
-
|
|
427
|
-
The CSV string to parse.
|
|
408
|
+
Creates a tag function
|
|
409
|
+
for escaping interpolated values in template literals.
|
|
428
410
|
|
|
429
|
-
####
|
|
411
|
+
#### escapeFn
|
|
430
412
|
|
|
431
|
-
|
|
413
|
+
A function that takes a value
|
|
414
|
+
and returns its escaped string representation.
|
|
432
415
|
|
|
433
416
|
#### Returns
|
|
434
417
|
|
|
435
|
-
A
|
|
436
|
-
|
|
418
|
+
A tag function that escapes interpolated values
|
|
419
|
+
using the provided escape map.
|
|
437
420
|
|
|
438
421
|
#### Usage Examples
|
|
439
422
|
|
|
440
423
|
```ts
|
|
441
|
-
const
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
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'
|
|
447
438
|
|
|
448
|
-
|
|
439
|
+
const sql = createEscape(escapeFn)
|
|
449
440
|
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
[
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
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
458
|
|
|
459
|
-
|
|
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
|
+
)`
|
|
460
465
|
|
|
461
|
-
|
|
462
|
-
const fixPlTypography: (node: Node) => void;
|
|
466
|
+
expect(actual).to.equal(expected)
|
|
463
467
|
```
|
|
464
468
|
|
|
465
|
-
|
|
466
|
-
to a DOM subtree.
|
|
467
|
-
|
|
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.
|
|
475
|
-
|
|
476
|
-
#### node
|
|
477
|
-
|
|
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.
|
|
482
|
-
|
|
483
|
-
#### Usage Examples
|
|
469
|
+
### createPluralRules
|
|
484
470
|
|
|
485
471
|
```ts
|
|
486
|
-
const
|
|
487
|
-
'Pchnąć w tę łódź jeża lub ośm skrzyń fig ' +
|
|
488
|
-
'(zob. https://pl.wikipedia.org/wiki/Pangram).',
|
|
489
|
-
['br'],
|
|
490
|
-
['b', 'Zażółć gęślą jaźń.'],
|
|
491
|
-
)
|
|
492
|
-
|
|
493
|
-
fixPlTypography(p)
|
|
494
|
-
|
|
495
|
-
expect(p.innerHTML).to.deep.equal(
|
|
496
|
-
'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>')
|
|
472
|
+
const createPluralRules: (locale: string, forms: Partial<Record<Intl.LDMLPluralRule, string>>) => (value: number) => string;
|
|
503
473
|
```
|
|
504
474
|
|
|
505
|
-
|
|
475
|
+
Creates a function that
|
|
476
|
+
returns the appropriate noun form
|
|
477
|
+
based on a numeric value using `Intl.PluralRules`.
|
|
506
478
|
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
(tagOrNode: string | Node, ...args1: HArgs1[]): Node;
|
|
512
|
-
};
|
|
513
|
-
```
|
|
479
|
+
Different languages have different plural rules.
|
|
480
|
+
The `Intl.PluralRules` API provides locale-aware
|
|
481
|
+
plural category selection.
|
|
482
|
+
Possible categories are:
|
|
514
483
|
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
(
|
|
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)
|
|
518
492
|
|
|
519
|
-
####
|
|
493
|
+
#### locale
|
|
520
494
|
|
|
521
|
-
|
|
522
|
-
for a new element.
|
|
523
|
-
If a `Node`, that node is modified in place.
|
|
495
|
+
A BCP 47 language tag (e.g., `pl`, `en`).
|
|
524
496
|
|
|
525
|
-
####
|
|
497
|
+
#### forms
|
|
526
498
|
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
A value of `false` removes the attribute.
|
|
533
|
-
- `null`/`undefined`: ignored.
|
|
534
|
-
- `Node`: appended as a child.
|
|
535
|
-
- `string`/`number`: converted to a `Text` node
|
|
536
|
-
and appended.
|
|
537
|
-
- [`HArgs`](#HArgs) array: processed recursively.
|
|
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.
|
|
538
504
|
|
|
539
505
|
#### Returns
|
|
540
506
|
|
|
541
|
-
|
|
507
|
+
A function that takes a numeric value
|
|
508
|
+
and returns the appropriate noun form.
|
|
542
509
|
|
|
543
510
|
#### Usage Examples
|
|
544
511
|
|
|
545
512
|
```ts
|
|
546
|
-
const
|
|
513
|
+
const auto = createPluralRules('pl', {
|
|
514
|
+
one: 'auto',
|
|
515
|
+
few: 'auta',
|
|
516
|
+
other: 'aut',
|
|
517
|
+
})
|
|
547
518
|
|
|
548
|
-
expect(
|
|
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')
|
|
549
523
|
|
|
550
|
-
const
|
|
524
|
+
const car = createPluralRules('en', {
|
|
525
|
+
one: 'car',
|
|
526
|
+
other: 'cars',
|
|
527
|
+
})
|
|
551
528
|
|
|
552
|
-
|
|
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')
|
|
553
533
|
|
|
554
|
-
|
|
555
|
-
expect(b.outerHTML).to.equal('<b><i>text</i></b>')
|
|
534
|
+
const empty = createPluralRules('en', {})
|
|
556
535
|
|
|
557
|
-
|
|
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
|
+
```
|
|
558
541
|
|
|
559
|
-
|
|
560
|
-
.to.equal('<i class="some class">text</i>')
|
|
561
|
-
expect(b.outerHTML)
|
|
562
|
-
.to.equal('<b><i class="some class">text</i></b>')
|
|
542
|
+
### createTokenizer
|
|
563
543
|
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
.to.equal('<span>text</span>')
|
|
544
|
+
```ts
|
|
545
|
+
const createTokenizer: <M, T>(decorator: (chunk: string, metadata?: M) => T, ...specs: [M, string | RegExp][]) => (code: string) => T[];
|
|
546
|
+
```
|
|
568
547
|
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
expect(h('span', 42).outerHTML).to.equal('<span>42</span>')
|
|
548
|
+
A helper for building simple tokenizers
|
|
549
|
+
(see also [`nanolightTs`](#nanolightTs)).
|
|
572
550
|
|
|
573
|
-
|
|
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
|
-
expect(
|
|
580
|
-
h(
|
|
581
|
-
'div',
|
|
582
|
-
{ $style: { margin: 0, padding: 0 } },
|
|
583
|
-
).outerHTML,
|
|
584
|
-
).to.equal('<div style="margin: 0px; padding: 0px;"></div>')
|
|
551
|
+
#### decorator
|
|
585
552
|
|
|
586
|
-
|
|
587
|
-
|
|
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`.
|
|
588
559
|
|
|
589
|
-
|
|
590
|
-
expect(input2.value).to.equal('42')
|
|
560
|
+
#### specs
|
|
591
561
|
|
|
592
|
-
|
|
593
|
-
|
|
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.
|
|
594
567
|
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
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
|
+
|
|
690
|
+
### csvParse
|
|
691
|
+
|
|
692
|
+
```ts
|
|
693
|
+
const csvParse: (csv: string, separator?: string) => string[][];
|
|
694
|
+
```
|
|
695
|
+
|
|
696
|
+
Parses a CSV string
|
|
697
|
+
into a two-dimensional array of strings.
|
|
698
|
+
|
|
699
|
+
Supports quoted fields with escaped double quotes (`""`).
|
|
700
|
+
Carriage returns are normalized.
|
|
701
|
+
|
|
702
|
+
#### csv
|
|
703
|
+
|
|
704
|
+
The CSV string to parse.
|
|
705
|
+
|
|
706
|
+
#### separator
|
|
707
|
+
|
|
708
|
+
The field delimiter (default: `','`).
|
|
709
|
+
|
|
710
|
+
#### Returns
|
|
711
|
+
|
|
712
|
+
A 2D array where each inner array
|
|
713
|
+
represents a row of fields.
|
|
714
|
+
|
|
715
|
+
#### Usage Examples
|
|
716
|
+
|
|
717
|
+
```ts
|
|
718
|
+
const text = `"aaa
|
|
719
|
+
""aaa""
|
|
720
|
+
aaa",bbb, "ccc,ccc"
|
|
721
|
+
"xxx,xxx", "yyy
|
|
722
|
+
yyy",zzz
|
|
723
|
+
42 , "42" , 17
|
|
724
|
+
|
|
725
|
+
`
|
|
726
|
+
|
|
727
|
+
expect(csvParse(text)).to.deep.equal([
|
|
728
|
+
['aaa\n"aaa"\naaa', 'bbb', 'ccc,ccc'],
|
|
729
|
+
['xxx,xxx', 'yyy\nyyy', 'zzz'],
|
|
730
|
+
[' 42 ', '42', ' 17'],
|
|
731
|
+
])
|
|
732
|
+
```
|
|
733
|
+
|
|
734
|
+
### fixPlTypography
|
|
735
|
+
|
|
736
|
+
```ts
|
|
737
|
+
const fixPlTypography: (node: Node) => void;
|
|
738
|
+
```
|
|
739
|
+
|
|
740
|
+
Applies Polish-specific typographic corrections
|
|
741
|
+
to a DOM subtree.
|
|
742
|
+
|
|
743
|
+
This function prevents orphaned conjunctions
|
|
744
|
+
(single-letter words like “a”, “i”, “o”, “u”, “w”, “z”)
|
|
745
|
+
from appearing at the end of a line
|
|
746
|
+
by wrapping them with the following word
|
|
747
|
+
in a non-breaking span.
|
|
748
|
+
It also inserts zero-width spaces
|
|
749
|
+
after slashes and dots to allow line breaks.
|
|
750
|
+
|
|
751
|
+
#### node
|
|
752
|
+
|
|
753
|
+
The root DOM node to process.
|
|
754
|
+
All descendant text nodes are corrected recursively,
|
|
755
|
+
except those inside `IFRAME`, `NOSCRIPT`, `PRE`,
|
|
756
|
+
`SCRIPT`, `STYLE`, or `TEXTAREA` elements.
|
|
757
|
+
|
|
758
|
+
#### Usage Examples
|
|
759
|
+
|
|
760
|
+
```ts
|
|
761
|
+
const p = h(
|
|
762
|
+
'p',
|
|
763
|
+
'Pchnąć w tę łódź jeża lub ośm skrzyń fig ' +
|
|
764
|
+
'(zob. https://pl.wikipedia.org/wiki/Pangram).',
|
|
765
|
+
['br'],
|
|
766
|
+
['b', 'Zażółć gęślą jaźń.']
|
|
767
|
+
)
|
|
768
|
+
|
|
769
|
+
fixPlTypography(p)
|
|
770
|
+
|
|
771
|
+
expect(p.innerHTML).to.deep.equal(
|
|
772
|
+
'Pchnąć ' +
|
|
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
|
+
)
|
|
780
|
+
```
|
|
781
|
+
|
|
782
|
+
### h
|
|
783
|
+
|
|
784
|
+
```ts
|
|
785
|
+
const h: {
|
|
786
|
+
<T extends keyof HTMLElementTagNameMap>(tag: T, ...args1: HArgs1[]): HTMLElementTagNameMap[T];
|
|
787
|
+
<N extends Node>(node: N, ...args1: HArgs1[]): N;
|
|
788
|
+
(tagOrNode: string | Node, ...args1: HArgs1[]): Node;
|
|
789
|
+
};
|
|
790
|
+
```
|
|
791
|
+
|
|
792
|
+
A lightweight *HyperScript*-style helper
|
|
793
|
+
for creating and modifying `HTMLElement`s
|
|
794
|
+
(see also [`s`](#s)).
|
|
795
|
+
|
|
796
|
+
#### tagOrNode
|
|
797
|
+
|
|
798
|
+
If a `string`, it is treated as the tag name
|
|
799
|
+
for a new element.
|
|
800
|
+
If a `Node`, that node is modified in place.
|
|
801
|
+
|
|
802
|
+
#### args
|
|
803
|
+
|
|
804
|
+
Additional arguments processed as follows:
|
|
805
|
+
- `Object`: maps attributes/properties.
|
|
806
|
+
Keys starting with `$` set element properties
|
|
807
|
+
(without the `$` prefix);
|
|
808
|
+
other keys set attributes via `setAttribute`.
|
|
809
|
+
A value of `false` removes the attribute.
|
|
810
|
+
- `null`/`undefined`: ignored.
|
|
811
|
+
- `Node`: appended as a child.
|
|
812
|
+
- `string`/`number`: converted to a `Text` node
|
|
813
|
+
and appended.
|
|
814
|
+
- [`HArgs`](#HArgs) array: processed recursively.
|
|
815
|
+
|
|
816
|
+
#### Returns
|
|
817
|
+
|
|
818
|
+
The created or modified `HTMLElement`.
|
|
819
|
+
|
|
820
|
+
#### Usage Examples
|
|
821
|
+
|
|
822
|
+
```ts
|
|
823
|
+
const b = h('b')
|
|
824
|
+
|
|
825
|
+
expect(b.outerHTML).to.equal('<b></b>')
|
|
826
|
+
|
|
827
|
+
const i = h('i', 'text')
|
|
828
|
+
|
|
829
|
+
h(b, i)
|
|
830
|
+
|
|
831
|
+
expect(i.outerHTML).to.equal('<i>text</i>')
|
|
832
|
+
|
|
833
|
+
expect(b.outerHTML).to.equal('<b><i>text</i></b>')
|
|
834
|
+
|
|
835
|
+
h(i, { $className: 'some class' })
|
|
836
|
+
|
|
837
|
+
expect(i.outerHTML).to.equal(
|
|
838
|
+
'<i class="some class">text</i>'
|
|
839
|
+
)
|
|
840
|
+
|
|
841
|
+
expect(b.outerHTML).to.equal(
|
|
842
|
+
'<b><i class="some class">text</i></b>'
|
|
843
|
+
)
|
|
844
|
+
|
|
845
|
+
expect(h('span', 'text').outerHTML).to.equal(
|
|
846
|
+
'<span>text</span>'
|
|
598
847
|
)
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
848
|
+
|
|
849
|
+
expect(
|
|
850
|
+
h('span', { $innerText: 'text' }).outerHTML
|
|
851
|
+
).to.equal('<span>text</span>')
|
|
852
|
+
|
|
853
|
+
expect(h('span', '42').outerHTML).to.equal(
|
|
854
|
+
'<span>42</span>'
|
|
602
855
|
)
|
|
603
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 })
|
|
872
|
+
const input2 = h('input', { $value: '42' })
|
|
873
|
+
|
|
874
|
+
expect(input1.value).to.equal('42')
|
|
875
|
+
expect(input2.value).to.equal('42')
|
|
876
|
+
|
|
877
|
+
expect(input1.outerHTML).to.equal('<input value="42">')
|
|
878
|
+
expect(input2.outerHTML).to.equal('<input>')
|
|
879
|
+
|
|
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
|
+
})
|
|
889
|
+
|
|
604
890
|
expect(checkbox1.checked).to.be.true
|
|
605
891
|
expect(checkbox2.checked).to.be.true
|
|
606
892
|
|
|
607
|
-
expect(checkbox1.outerHTML)
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
893
|
+
expect(checkbox1.outerHTML).to.equal(
|
|
894
|
+
'<input type="checkbox" checked="">'
|
|
895
|
+
)
|
|
896
|
+
|
|
897
|
+
expect(checkbox2.outerHTML).to.equal(
|
|
898
|
+
'<input type="checkbox">'
|
|
899
|
+
)
|
|
611
900
|
|
|
612
901
|
const div = h('div')
|
|
613
902
|
|
|
@@ -628,15 +917,20 @@ expect(elemWithClass.getAttribute('class')).to.equal('test')
|
|
|
628
917
|
const elemWithText = h('div', 'initial')
|
|
629
918
|
|
|
630
919
|
h(elemWithText, ' more')
|
|
631
|
-
expect(elemWithText.outerHTML)
|
|
632
|
-
.to.equal('<div>initial more</div>')
|
|
633
920
|
|
|
634
|
-
|
|
921
|
+
expect(elemWithText.outerHTML).to.equal(
|
|
922
|
+
'<div>initial more</div>'
|
|
923
|
+
)
|
|
924
|
+
|
|
925
|
+
const elemWithNested = h(
|
|
926
|
+
'div',
|
|
635
927
|
['span', 'hello'],
|
|
636
|
-
['b', 'world']
|
|
928
|
+
['b', 'world']
|
|
929
|
+
)
|
|
637
930
|
|
|
638
|
-
expect(elemWithNested.outerHTML)
|
|
639
|
-
|
|
931
|
+
expect(elemWithNested.outerHTML).to.equal(
|
|
932
|
+
'<div><span>hello</span><b>world</b></div>'
|
|
933
|
+
)
|
|
640
934
|
```
|
|
641
935
|
|
|
642
936
|
### hasOwn
|
|
@@ -721,8 +1015,9 @@ const fakeArray1 = Object.create({ constructor: Array })
|
|
|
721
1015
|
|
|
722
1016
|
expect(isArray(fakeArray1)).to.be.false
|
|
723
1017
|
|
|
724
|
-
const fakeArray2 =
|
|
725
|
-
|
|
1018
|
+
const fakeArray2 = Object.create({
|
|
1019
|
+
[Symbol.toStringTag]: Array.name,
|
|
1020
|
+
})
|
|
726
1021
|
|
|
727
1022
|
expect(isArray(fakeArray2)).to.be.false
|
|
728
1023
|
```
|
|
@@ -748,9 +1043,13 @@ The value to check.
|
|
|
748
1043
|
|
|
749
1044
|
```ts
|
|
750
1045
|
expect(isFiniteNumber(42)).to.be.true
|
|
1046
|
+
|
|
751
1047
|
expect(isFiniteNumber(Number(42))).to.be.true
|
|
1048
|
+
|
|
752
1049
|
expect(isFiniteNumber(new Number(42))).to.be.false
|
|
1050
|
+
|
|
753
1051
|
expect(isFiniteNumber(NaN)).to.be.false
|
|
1052
|
+
|
|
754
1053
|
expect(isFiniteNumber(Infinity)).to.be.false
|
|
755
1054
|
```
|
|
756
1055
|
|
|
@@ -774,12 +1073,18 @@ The value to check.
|
|
|
774
1073
|
|
|
775
1074
|
```ts
|
|
776
1075
|
expect(isInteger(42)).to.be.true
|
|
1076
|
+
|
|
777
1077
|
expect(isInteger(42.00000000000001)).to.be.false
|
|
1078
|
+
|
|
778
1079
|
expect(isInteger(42.000000000000001)).to.be.true
|
|
779
1080
|
// ^because of loss of precision
|
|
1081
|
+
|
|
780
1082
|
expect(isInteger(Number(42))).to.be.true
|
|
1083
|
+
|
|
781
1084
|
expect(isInteger(new Number(42))).to.be.false
|
|
1085
|
+
|
|
782
1086
|
expect(isInteger(NaN)).to.be.false
|
|
1087
|
+
|
|
783
1088
|
expect(isInteger(Infinity)).to.be.false
|
|
784
1089
|
```
|
|
785
1090
|
|
|
@@ -804,9 +1109,13 @@ The value to check.
|
|
|
804
1109
|
|
|
805
1110
|
```ts
|
|
806
1111
|
expect(isNumber(42)).to.be.true
|
|
1112
|
+
|
|
807
1113
|
expect(isNumber(Number(42))).to.be.true
|
|
1114
|
+
|
|
808
1115
|
expect(isNumber(new Number(42))).to.be.false
|
|
1116
|
+
|
|
809
1117
|
expect(isNumber(NaN)).to.be.true
|
|
1118
|
+
|
|
810
1119
|
expect(isNumber(Infinity)).to.be.true
|
|
811
1120
|
```
|
|
812
1121
|
|
|
@@ -831,495 +1140,220 @@ The value to check.
|
|
|
831
1140
|
|
|
832
1141
|
```ts
|
|
833
1142
|
expect(isRecord({})).to.be.true
|
|
834
|
-
expect(isRecord([])).to.be.false
|
|
835
|
-
expect(isRecord(Object.create(null))).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
|
-
|
|
846
|
-
expect(isRecord(new Number(42))).to.be.true
|
|
847
|
-
expect(isRecord(new String('42'))).to.be.true
|
|
848
|
-
|
|
849
|
-
class FooBar { }
|
|
850
|
-
|
|
851
|
-
expect(isRecord(new FooBar())).to.be.true
|
|
852
|
-
```
|
|
853
|
-
|
|
854
|
-
### isString
|
|
855
|
-
|
|
856
|
-
```ts
|
|
857
|
-
const isString: (arg: unknown) => arg is string;
|
|
858
|
-
```
|
|
859
|
-
|
|
860
|
-
Checks whether the argument is a string.
|
|
861
|
-
|
|
862
|
-
#### arg
|
|
863
|
-
|
|
864
|
-
The value to check.
|
|
865
|
-
|
|
866
|
-
#### Returns
|
|
867
|
-
|
|
868
|
-
`true` if `typeof arg === 'string'`, `false` otherwise.
|
|
869
|
-
|
|
870
|
-
#### Usage Examples
|
|
871
|
-
|
|
872
|
-
```ts
|
|
873
|
-
expect(isString('42')).to.be.true
|
|
874
|
-
expect(isString(String('42'))).to.be.true
|
|
875
|
-
expect(isString(new String('42'))).to.be.false
|
|
876
|
-
```
|
|
877
|
-
|
|
878
|
-
### jsOnParse
|
|
879
|
-
|
|
880
|
-
```ts
|
|
881
|
-
const jsOnParse: (handlers: Record<PropertyKey, Function>, text: string) => any;
|
|
882
|
-
```
|
|
883
|
-
|
|
884
|
-
Parses JSON with support for
|
|
885
|
-
handler-based value transformation (“JavaScript ON”).
|
|
886
|
-
|
|
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.
|
|
892
|
-
|
|
893
|
-
#### handlers
|
|
894
|
-
|
|
895
|
-
An object mapping handler names to functions.
|
|
896
|
-
|
|
897
|
-
#### text
|
|
898
|
-
|
|
899
|
-
The JSON string to parse.
|
|
900
|
-
|
|
901
|
-
#### Returns
|
|
902
|
-
|
|
903
|
-
The parsed value with handler substitutions applied.
|
|
904
|
-
|
|
905
|
-
#### Usage Examples
|
|
906
|
-
|
|
907
|
-
```ts
|
|
908
|
-
const handlers = {
|
|
909
|
-
$add: (a: number, b: number) => a + b,
|
|
910
|
-
$hello: (name: string) => `Hello ${name}!`,
|
|
911
|
-
$foo: () => 'bar',
|
|
912
|
-
}
|
|
913
1143
|
|
|
914
|
-
|
|
915
|
-
{
|
|
916
|
-
"$add": [1, 2]
|
|
917
|
-
},
|
|
918
|
-
{
|
|
919
|
-
"$hello": ["World"]
|
|
920
|
-
},
|
|
921
|
-
{
|
|
922
|
-
"nested": {
|
|
923
|
-
"$hello": ["nested World"]
|
|
924
|
-
},
|
|
925
|
-
"one": 1,
|
|
926
|
-
"two": 2
|
|
927
|
-
},
|
|
928
|
-
{
|
|
929
|
-
"$foo": []
|
|
930
|
-
},
|
|
931
|
-
{
|
|
932
|
-
"$foo": ["The parent object does not have exactly one property!"],
|
|
933
|
-
"one": 1,
|
|
934
|
-
"two": 2
|
|
935
|
-
}
|
|
936
|
-
]`)
|
|
937
|
-
|
|
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
|
-
)
|
|
958
|
-
|
|
959
|
-
const actual2 = jsOnParse(
|
|
960
|
-
{ $notFunc: 'handler not being a function' } as any,
|
|
961
|
-
'{"$notFunc": [1, 2]}',
|
|
962
|
-
)
|
|
963
|
-
|
|
964
|
-
expect(actual2).to.deep.equal({ $notFunc: [1, 2] })
|
|
965
|
-
|
|
966
|
-
const actual3 = jsOnParse(
|
|
967
|
-
handlers,
|
|
968
|
-
'{"$unknown_handler_key": [1, 2]}',
|
|
969
|
-
)
|
|
970
|
-
|
|
971
|
-
expect(actual3)
|
|
972
|
-
.to.deep.equal({ $unknown_handler_key: [1, 2] })
|
|
973
|
-
|
|
974
|
-
const actual4 = jsOnParse(
|
|
975
|
-
handlers,
|
|
976
|
-
'{"$add": {"not": "array"}}',
|
|
977
|
-
)
|
|
978
|
-
|
|
979
|
-
expect(actual4)
|
|
980
|
-
.to.deep.equal({ $add: { not: 'array' } })
|
|
981
|
-
```
|
|
982
|
-
|
|
983
|
-
### monokai
|
|
984
|
-
|
|
985
|
-
```ts
|
|
986
|
-
const monokai: CRoot;
|
|
987
|
-
```
|
|
988
|
-
|
|
989
|
-
A Monokai-inspired color scheme
|
|
990
|
-
for use with the [`c`](#c) helper
|
|
991
|
-
and [`nanolightTs`](#nanolightTs) tokenizer.
|
|
992
|
-
|
|
993
|
-
### nanolightTs
|
|
994
|
-
|
|
995
|
-
```ts
|
|
996
|
-
const nanolightTs: (code: string) => HArgs1[];
|
|
997
|
-
```
|
|
998
|
-
|
|
999
|
-
A TypeScript/JavaScript syntax highlighting tokenizer
|
|
1000
|
-
built using [`newTokenizer`](#newTokenizer).
|
|
1001
|
-
|
|
1002
|
-
#### code
|
|
1003
|
-
|
|
1004
|
-
The source code string to tokenize.
|
|
1005
|
-
|
|
1006
|
-
#### Returns
|
|
1007
|
-
|
|
1008
|
-
An array of [`HArgs1`](#HArgs1) elements
|
|
1009
|
-
suitable for rendering with [`h`](#h).
|
|
1010
|
-
|
|
1011
|
-
#### Usage Examples
|
|
1012
|
-
|
|
1013
|
-
```ts
|
|
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
|
-
)
|
|
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)
|
|
1144
|
+
expect(isRecord([])).to.be.false
|
|
1134
1145
|
|
|
1135
|
-
|
|
1146
|
+
expect(isRecord(Object.create(null))).to.be.true
|
|
1136
1147
|
|
|
1137
|
-
|
|
1148
|
+
const fakeNumber1 = Object.create({ constructor: Number })
|
|
1138
1149
|
|
|
1139
|
-
|
|
1150
|
+
expect(isRecord(fakeNumber1)).to.be.true
|
|
1140
1151
|
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
the function falls back to `other`,
|
|
1145
|
-
then to an empty string.
|
|
1152
|
+
const fakeNumber2 = Object.create({
|
|
1153
|
+
[Symbol.toStringTag]: Number.name,
|
|
1154
|
+
})
|
|
1146
1155
|
|
|
1147
|
-
|
|
1156
|
+
expect(isRecord(fakeNumber2)).to.be.true
|
|
1148
1157
|
|
|
1149
|
-
|
|
1150
|
-
and returns the appropriate noun form.
|
|
1158
|
+
expect(isRecord(new Number(42))).to.be.true
|
|
1151
1159
|
|
|
1152
|
-
|
|
1160
|
+
expect(isRecord(new String('42'))).to.be.true
|
|
1161
|
+
|
|
1162
|
+
class FooBar {}
|
|
1163
|
+
|
|
1164
|
+
expect(isRecord(new FooBar())).to.be.true
|
|
1165
|
+
```
|
|
1166
|
+
|
|
1167
|
+
### isString
|
|
1153
1168
|
|
|
1154
1169
|
```ts
|
|
1155
|
-
const
|
|
1156
|
-
|
|
1157
|
-
{ one: 'auto', few: 'auta', other: 'aut' },
|
|
1158
|
-
)
|
|
1170
|
+
const isString: (arg: unknown) => arg is string;
|
|
1171
|
+
```
|
|
1159
1172
|
|
|
1160
|
-
|
|
1161
|
-
expect(auto(1)).to.equal('auto')
|
|
1162
|
-
expect(auto(17)).to.equal('aut')
|
|
1163
|
-
expect(auto(42)).to.equal('auta')
|
|
1173
|
+
Checks whether the argument is a string.
|
|
1164
1174
|
|
|
1165
|
-
|
|
1175
|
+
#### arg
|
|
1166
1176
|
|
|
1167
|
-
|
|
1168
|
-
expect(car(1)).to.equal('car')
|
|
1169
|
-
expect(car(17)).to.equal('cars')
|
|
1170
|
-
expect(car(42)).to.equal('cars')
|
|
1177
|
+
The value to check.
|
|
1171
1178
|
|
|
1172
|
-
|
|
1179
|
+
#### Returns
|
|
1173
1180
|
|
|
1174
|
-
|
|
1175
|
-
expect(empty(1)).to.equal('')
|
|
1176
|
-
expect(empty(17)).to.equal('')
|
|
1177
|
-
expect(empty(42)).to.equal('')
|
|
1178
|
-
```
|
|
1181
|
+
`true` if `typeof arg === 'string'`, `false` otherwise.
|
|
1179
1182
|
|
|
1180
|
-
|
|
1183
|
+
#### Usage Examples
|
|
1181
1184
|
|
|
1182
1185
|
```ts
|
|
1183
|
-
|
|
1186
|
+
expect(isString('42')).to.be.true
|
|
1187
|
+
expect(isString(String('42'))).to.be.true
|
|
1188
|
+
expect(isString(new String('42'))).to.be.false
|
|
1184
1189
|
```
|
|
1185
1190
|
|
|
1186
|
-
|
|
1187
|
-
(see also [`nanolightTs`](#nanolightTs)).
|
|
1191
|
+
### jsOnParse
|
|
1188
1192
|
|
|
1189
|
-
|
|
1193
|
+
```ts
|
|
1194
|
+
const jsOnParse: (handlers: Record<PropertyKey, Function>, text: string) => any;
|
|
1195
|
+
```
|
|
1190
1196
|
|
|
1191
|
-
|
|
1192
|
-
|
|
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
|
+
Parses JSON with support for
|
|
1198
|
+
handler-based value transformation (“JavaScript ON”).
|
|
1197
1199
|
|
|
1198
|
-
|
|
1200
|
+
Objects with exactly one property
|
|
1201
|
+
whose key exists in `handlers`
|
|
1202
|
+
and whose value is an array
|
|
1203
|
+
are replaced by invoking the corresponding handler
|
|
1204
|
+
with the array elements as arguments.
|
|
1199
1205
|
|
|
1200
|
-
|
|
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.
|
|
1206
|
+
#### handlers
|
|
1205
1207
|
|
|
1206
|
-
|
|
1208
|
+
An object mapping handler names to functions.
|
|
1207
1209
|
|
|
1208
|
-
|
|
1209
|
-
and returns an array of decorated tokens.
|
|
1210
|
+
#### text
|
|
1210
1211
|
|
|
1211
|
-
|
|
1212
|
+
The JSON string to parse.
|
|
1212
1213
|
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
the longer one wins.
|
|
1217
|
-
3. Among matches of the same position and length,
|
|
1218
|
-
the one defined earlier wins.
|
|
1214
|
+
#### Returns
|
|
1215
|
+
|
|
1216
|
+
The parsed value with handler substitutions applied.
|
|
1219
1217
|
|
|
1220
1218
|
#### Usage Examples
|
|
1221
1219
|
|
|
1222
1220
|
```ts
|
|
1223
|
-
const
|
|
1224
|
-
(
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
const result1 = tokenizer1('if "hello" else "world"')
|
|
1221
|
+
const handlers = {
|
|
1222
|
+
$add: (a: number, b: number) => a + b,
|
|
1223
|
+
$hello: (name: string) => `Hello ${name}!`,
|
|
1224
|
+
$foo: () => 'bar',
|
|
1225
|
+
}
|
|
1229
1226
|
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1227
|
+
const actual1 = jsOnParse(
|
|
1228
|
+
handlers,
|
|
1229
|
+
`[
|
|
1230
|
+
{
|
|
1231
|
+
"$add": [1, 2]
|
|
1232
|
+
},
|
|
1233
|
+
{
|
|
1234
|
+
"$hello": ["World"]
|
|
1235
|
+
},
|
|
1236
|
+
{
|
|
1237
|
+
"nested": {
|
|
1238
|
+
"$hello": ["nested World"]
|
|
1239
|
+
},
|
|
1240
|
+
"one": 1,
|
|
1241
|
+
"two": 2
|
|
1242
|
+
},
|
|
1243
|
+
{
|
|
1244
|
+
"$foo": []
|
|
1245
|
+
},
|
|
1246
|
+
{
|
|
1247
|
+
"$foo": ["The parent object does not have exactly one property!"],
|
|
1248
|
+
"one": 1,
|
|
1249
|
+
"two": 2
|
|
1250
|
+
}
|
|
1251
|
+
]`
|
|
1240
1252
|
)
|
|
1241
1253
|
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1254
|
+
expect(actual1).to.deep.equal([
|
|
1255
|
+
3,
|
|
1256
|
+
'Hello World!',
|
|
1257
|
+
{
|
|
1258
|
+
nested: 'Hello nested World!',
|
|
1259
|
+
one: 1,
|
|
1260
|
+
two: 2,
|
|
1261
|
+
},
|
|
1262
|
+
'bar',
|
|
1263
|
+
{
|
|
1264
|
+
$foo: [
|
|
1265
|
+
'The parent object does not have ' +
|
|
1266
|
+
'exactly one property!',
|
|
1267
|
+
],
|
|
1268
|
+
one: 1,
|
|
1269
|
+
two: 2,
|
|
1270
|
+
},
|
|
1271
|
+
])
|
|
1248
1272
|
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
'tag:BEGIN',
|
|
1253
|
-
'undefined:b',
|
|
1254
|
-
'end:END',
|
|
1255
|
-
'undefined:c',
|
|
1256
|
-
],
|
|
1273
|
+
const actual2 = jsOnParse(
|
|
1274
|
+
{ $notFunc: 'handler not being a function' } as any,
|
|
1275
|
+
'{"$notFunc": [1, 2]}'
|
|
1257
1276
|
)
|
|
1258
1277
|
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1278
|
+
expect(actual2).to.deep.equal({ $notFunc: [1, 2] })
|
|
1279
|
+
|
|
1280
|
+
const actual3 = jsOnParse(
|
|
1281
|
+
handlers,
|
|
1282
|
+
'{"$unknown_handler_key": [1, 2]}'
|
|
1262
1283
|
)
|
|
1263
|
-
const result3 = tokenizer3('')
|
|
1264
1284
|
|
|
1265
|
-
expect(
|
|
1285
|
+
expect(actual3).to.deep.equal({
|
|
1286
|
+
$unknown_handler_key: [1, 2],
|
|
1287
|
+
})
|
|
1266
1288
|
|
|
1267
|
-
const
|
|
1268
|
-
|
|
1269
|
-
|
|
1289
|
+
const actual4 = jsOnParse(
|
|
1290
|
+
handlers,
|
|
1291
|
+
'{"$add": {"not": "array"}}'
|
|
1270
1292
|
)
|
|
1271
|
-
const result4 = tokenizer4('test here')
|
|
1272
1293
|
|
|
1273
|
-
expect(
|
|
1274
|
-
|
|
1275
|
-
{ chunk: 'test', metadata: 'start' },
|
|
1276
|
-
{ chunk: ' here', metadata: undefined },
|
|
1277
|
-
],
|
|
1278
|
-
)
|
|
1294
|
+
expect(actual4).to.deep.equal({ $add: { not: 'array' } })
|
|
1295
|
+
```
|
|
1279
1296
|
|
|
1280
|
-
|
|
1281
|
-
(chunk, metadata) => metadata,
|
|
1282
|
-
['later', /x/],
|
|
1283
|
-
['earlier', /y/],
|
|
1284
|
-
)
|
|
1285
|
-
const result5 = tokenizer5('yx')
|
|
1297
|
+
### monokai
|
|
1286
1298
|
|
|
1287
|
-
|
|
1299
|
+
```ts
|
|
1300
|
+
const monokai: CRoot;
|
|
1301
|
+
```
|
|
1288
1302
|
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
['long', 'abc'],
|
|
1293
|
-
)
|
|
1294
|
-
const result6 = tokenizer6('abc')
|
|
1303
|
+
A Monokai-inspired color scheme
|
|
1304
|
+
for use with the [`c`](#c) helper
|
|
1305
|
+
and [`nanolightTs`](#nanolightTs) tokenizer.
|
|
1295
1306
|
|
|
1296
|
-
|
|
1307
|
+
### nanolightTs
|
|
1297
1308
|
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
['word', /\w+/],
|
|
1302
|
-
)
|
|
1303
|
-
const result7 = tokenizer7('hello')
|
|
1309
|
+
```ts
|
|
1310
|
+
const nanolightTs: (code: string) => HArgs1[];
|
|
1311
|
+
```
|
|
1304
1312
|
|
|
1305
|
-
|
|
1313
|
+
A TypeScript/JavaScript syntax highlighting tokenizer
|
|
1314
|
+
built using [`createTokenizer`](#createTokenizer).
|
|
1306
1315
|
|
|
1307
|
-
|
|
1308
|
-
(chunk) => chunk,
|
|
1309
|
-
['test', /xyz/],
|
|
1310
|
-
)
|
|
1311
|
-
const result8 = tokenizer8('abc')
|
|
1316
|
+
#### code
|
|
1312
1317
|
|
|
1313
|
-
|
|
1318
|
+
The source code string to tokenize.
|
|
1314
1319
|
|
|
1315
|
-
|
|
1316
|
-
(_chunk, metadata) => metadata,
|
|
1317
|
-
['a', 'a'],
|
|
1318
|
-
['b', 'b'],
|
|
1319
|
-
)
|
|
1320
|
-
const result9 = tokenizer9('aabb')
|
|
1320
|
+
#### Returns
|
|
1321
1321
|
|
|
1322
|
-
|
|
1322
|
+
An array of [`HArgs1`](#HArgs1) elements
|
|
1323
|
+
suitable for rendering with [`h`](#h).
|
|
1324
|
+
|
|
1325
|
+
#### Usage Examples
|
|
1326
|
+
|
|
1327
|
+
```ts
|
|
1328
|
+
const codeJs =
|
|
1329
|
+
'const answerToLifeTheUniverseAndEverything = ' +
|
|
1330
|
+
"{ 42: 42 }['42'] /* 42 */"
|
|
1331
|
+
|
|
1332
|
+
expect(nanolightTs(codeJs)).to.deep.equal([
|
|
1333
|
+
['span', { class: 'keyword-1' }, 'const'],
|
|
1334
|
+
' ',
|
|
1335
|
+
[
|
|
1336
|
+
'span',
|
|
1337
|
+
{ class: 'identifier-4' },
|
|
1338
|
+
'answerToLifeTheUniverseAndEverything',
|
|
1339
|
+
],
|
|
1340
|
+
' ',
|
|
1341
|
+
['span', { class: 'operator' }, '='],
|
|
1342
|
+
' ',
|
|
1343
|
+
['span', { class: 'punctuation' }, '{'],
|
|
1344
|
+
' ',
|
|
1345
|
+
['span', { class: 'number' }, '42'],
|
|
1346
|
+
['span', { class: 'operator' }, ':'],
|
|
1347
|
+
' ',
|
|
1348
|
+
['span', { class: 'number' }, '42'],
|
|
1349
|
+
' ',
|
|
1350
|
+
['span', { class: 'punctuation' }, '}'],
|
|
1351
|
+
['span', { class: 'punctuation' }, '['],
|
|
1352
|
+
['span', { class: 'string' }, "'42'"],
|
|
1353
|
+
['span', { class: 'punctuation' }, ']'],
|
|
1354
|
+
' ',
|
|
1355
|
+
['span', { class: 'comment' }, '/* 42 */'],
|
|
1356
|
+
])
|
|
1323
1357
|
```
|
|
1324
1358
|
|
|
1325
1359
|
### omit
|
|
@@ -1385,9 +1419,10 @@ A new object with only the specified keys.
|
|
|
1385
1419
|
```ts
|
|
1386
1420
|
const ref = { a: 42, b: '42', c: 17 }
|
|
1387
1421
|
|
|
1388
|
-
expect(pick(ref, ['a', 'b'])).to.deep.equal(
|
|
1389
|
-
|
|
1390
|
-
|
|
1422
|
+
expect(pick(ref, ['a', 'b'])).to.deep.equal({
|
|
1423
|
+
a: 42,
|
|
1424
|
+
b: '42',
|
|
1425
|
+
})
|
|
1391
1426
|
```
|
|
1392
1427
|
|
|
1393
1428
|
### rwd
|
|
@@ -1442,36 +1477,34 @@ const style: CRoot = {
|
|
|
1442
1477
|
|
|
1443
1478
|
rwd(style, '.r6', 200, 50, [6], [3], [1, 1, 2])
|
|
1444
1479
|
|
|
1445
|
-
expect(style).to.deep.equal(
|
|
1446
|
-
{
|
|
1447
|
-
|
|
1448
|
-
|
|
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',
|
|
1449
1488
|
},
|
|
1489
|
+
boxSizing: 'border-box',
|
|
1490
|
+
display: 'block',
|
|
1491
|
+
float: 'left',
|
|
1492
|
+
width: '100%',
|
|
1493
|
+
height: '100px',
|
|
1494
|
+
},
|
|
1495
|
+
'@media(min-width:600px)': {
|
|
1450
1496
|
'.r6': {
|
|
1451
|
-
|
|
1452
|
-
'
|
|
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
|
-
},
|
|
1497
|
+
width: 'calc(100% / 3)',
|
|
1498
|
+
height: '50px',
|
|
1466
1499
|
},
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1500
|
+
},
|
|
1501
|
+
'@media(min-width:1200px)': {
|
|
1502
|
+
'.r6': {
|
|
1503
|
+
width: 'calc(50% / 3)',
|
|
1504
|
+
height: '50px',
|
|
1472
1505
|
},
|
|
1473
1506
|
},
|
|
1474
|
-
)
|
|
1507
|
+
})
|
|
1475
1508
|
```
|
|
1476
1509
|
|
|
1477
1510
|
### s
|
|
@@ -1525,18 +1558,19 @@ const svg2 = s('svg', { 'xlink:href': false })
|
|
|
1525
1558
|
|
|
1526
1559
|
expect(svg2.getAttributeNS(XLINK_NS, 'href')).to.be.null
|
|
1527
1560
|
|
|
1528
|
-
const svg3 = s(
|
|
1529
|
-
'
|
|
1530
|
-
|
|
1531
|
-
)
|
|
1561
|
+
const svg3 = s('svg', {
|
|
1562
|
+
'xlink:href': 'http://example.com',
|
|
1563
|
+
})
|
|
1532
1564
|
|
|
1533
|
-
expect(svg3.getAttributeNS(XLINK_NS, 'href'))
|
|
1534
|
-
|
|
1565
|
+
expect(svg3.getAttributeNS(XLINK_NS, 'href')).to.equal(
|
|
1566
|
+
'http://example.com'
|
|
1567
|
+
)
|
|
1535
1568
|
|
|
1536
1569
|
const svg4 = s('svg', { 'xlink:title': 42 })
|
|
1537
1570
|
|
|
1538
|
-
expect(svg4.getAttributeNS(XLINK_NS, 'title'))
|
|
1539
|
-
|
|
1571
|
+
expect(svg4.getAttributeNS(XLINK_NS, 'title')).to.equal(
|
|
1572
|
+
'42'
|
|
1573
|
+
)
|
|
1540
1574
|
```
|
|
1541
1575
|
|
|
1542
1576
|
### svgUse
|
|
@@ -1575,51 +1609,63 @@ expect(svg.children.length).to.equal(1)
|
|
|
1575
1609
|
|
|
1576
1610
|
const useElement = svg.children[0]
|
|
1577
1611
|
|
|
1578
|
-
expect(
|
|
1579
|
-
.
|
|
1612
|
+
expect(
|
|
1613
|
+
useElement.getAttributeNS(XLINK_NS, 'href')
|
|
1614
|
+
).to.equal('#icon-home')
|
|
1580
1615
|
|
|
1581
|
-
const svgWithViewBox =
|
|
1582
|
-
|
|
1616
|
+
const svgWithViewBox = svgUse('icon-star', {
|
|
1617
|
+
viewBox: '0 0 24 24',
|
|
1618
|
+
})
|
|
1583
1619
|
|
|
1584
|
-
expect(svgWithViewBox.getAttribute('viewBox'))
|
|
1585
|
-
|
|
1620
|
+
expect(svgWithViewBox.getAttribute('viewBox')).to.equal(
|
|
1621
|
+
'0 0 24 24'
|
|
1622
|
+
)
|
|
1586
1623
|
|
|
1587
1624
|
const useViewBox = svgWithViewBox.children[0]
|
|
1588
1625
|
|
|
1589
|
-
expect(
|
|
1590
|
-
.
|
|
1626
|
+
expect(
|
|
1627
|
+
useViewBox.getAttributeNS(XLINK_NS, 'href')
|
|
1628
|
+
).to.equal('#icon-star')
|
|
1591
1629
|
|
|
1592
|
-
const svgWithClass =
|
|
1593
|
-
|
|
1630
|
+
const svgWithClass = svgUse('icon-menu', {
|
|
1631
|
+
class: 'icon-btn',
|
|
1632
|
+
})
|
|
1594
1633
|
|
|
1595
|
-
expect(svgWithClass.getAttribute('class'))
|
|
1596
|
-
|
|
1634
|
+
expect(svgWithClass.getAttribute('class')).to.equal(
|
|
1635
|
+
'icon-btn'
|
|
1636
|
+
)
|
|
1597
1637
|
|
|
1598
1638
|
expect(svgWithClass.children.length).to.equal(1)
|
|
1599
1639
|
|
|
1600
1640
|
const useClass = svgWithClass.children[0]
|
|
1601
1641
|
|
|
1602
|
-
expect(useClass.getAttributeNS(XLINK_NS, 'href'))
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
const svgWithMultipleAttrs = svgUse(
|
|
1606
|
-
'icon-settings',
|
|
1607
|
-
{ width: 24, height: 24, class: 'icon' },
|
|
1642
|
+
expect(useClass.getAttributeNS(XLINK_NS, 'href')).to.equal(
|
|
1643
|
+
'#icon-menu'
|
|
1608
1644
|
)
|
|
1609
1645
|
|
|
1610
|
-
|
|
1611
|
-
|
|
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
|
+
)
|
|
1612
1655
|
|
|
1613
|
-
expect(
|
|
1614
|
-
.
|
|
1656
|
+
expect(
|
|
1657
|
+
svgWithMultipleAttrs.getAttribute('height')
|
|
1658
|
+
).to.equal('24')
|
|
1615
1659
|
|
|
1616
|
-
expect(svgWithMultipleAttrs.getAttribute('class'))
|
|
1617
|
-
|
|
1660
|
+
expect(svgWithMultipleAttrs.getAttribute('class')).to.equal(
|
|
1661
|
+
'icon'
|
|
1662
|
+
)
|
|
1618
1663
|
|
|
1619
1664
|
const useMultiple = svgWithMultipleAttrs.children[0]
|
|
1620
1665
|
|
|
1621
|
-
expect(
|
|
1622
|
-
.
|
|
1666
|
+
expect(
|
|
1667
|
+
useMultiple.getAttributeNS(XLINK_NS, 'href')
|
|
1668
|
+
).to.equal('#icon-settings')
|
|
1623
1669
|
```
|
|
1624
1670
|
|
|
1625
1671
|
### uuidV1
|
|
@@ -1683,17 +1729,19 @@ for (let i = 1; i <= 22136; ++i) {
|
|
|
1683
1729
|
}
|
|
1684
1730
|
}
|
|
1685
1731
|
|
|
1686
|
-
expect(
|
|
1687
|
-
.
|
|
1732
|
+
expect(
|
|
1733
|
+
uuidV1(new Date(), '000123456789abc').split('-')[4]
|
|
1734
|
+
).to.equal('123456789abc')
|
|
1688
1735
|
|
|
1689
|
-
expect(
|
|
1690
|
-
.
|
|
1736
|
+
expect(
|
|
1737
|
+
uuidV1(new Date(), '123456789').split('-')[4]
|
|
1738
|
+
).to.equal('000123456789')
|
|
1691
1739
|
|
|
1692
1740
|
expect(
|
|
1693
|
-
uuidV1(new Date(323325000000))
|
|
1694
|
-
|
|
1695
|
-
)
|
|
1696
|
-
|
|
1741
|
+
uuidV1(new Date(323325000000)).startsWith(
|
|
1742
|
+
'c1399400-9a71-11bd'
|
|
1743
|
+
)
|
|
1744
|
+
).to.be.true
|
|
1697
1745
|
```
|
|
1698
1746
|
|
|
1699
1747
|
### vivify
|
|
@@ -1749,58 +1797,48 @@ const ref: any = {}
|
|
|
1749
1797
|
|
|
1750
1798
|
vivify(ref).one.two[3][4]
|
|
1751
1799
|
|
|
1752
|
-
expect(ref).to.deep.equal(
|
|
1753
|
-
{
|
|
1754
|
-
|
|
1755
|
-
two: [undefined, undefined, undefined, []],
|
|
1756
|
-
},
|
|
1800
|
+
expect(ref).to.deep.equal({
|
|
1801
|
+
one: {
|
|
1802
|
+
two: [undefined, undefined, undefined, []],
|
|
1757
1803
|
},
|
|
1758
|
-
)
|
|
1804
|
+
})
|
|
1759
1805
|
|
|
1760
1806
|
vivify(ref).one.two[3][4] = 5
|
|
1761
1807
|
|
|
1762
|
-
expect(ref).to.deep.equal(
|
|
1763
|
-
{
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
],
|
|
1771
|
-
},
|
|
1808
|
+
expect(ref).to.deep.equal({
|
|
1809
|
+
one: {
|
|
1810
|
+
two: [
|
|
1811
|
+
undefined,
|
|
1812
|
+
undefined,
|
|
1813
|
+
undefined,
|
|
1814
|
+
[undefined, undefined, undefined, undefined, 5],
|
|
1815
|
+
],
|
|
1772
1816
|
},
|
|
1773
|
-
)
|
|
1817
|
+
})
|
|
1774
1818
|
|
|
1775
1819
|
vivify(ref).one.two[3].length = 1
|
|
1776
1820
|
|
|
1777
|
-
expect(ref).to.deep.equal(
|
|
1778
|
-
{
|
|
1779
|
-
|
|
1780
|
-
two: [undefined, undefined, undefined, [undefined]],
|
|
1781
|
-
},
|
|
1821
|
+
expect(ref).to.deep.equal({
|
|
1822
|
+
one: {
|
|
1823
|
+
two: [undefined, undefined, undefined, [undefined]],
|
|
1782
1824
|
},
|
|
1783
|
-
)
|
|
1825
|
+
})
|
|
1784
1826
|
|
|
1785
1827
|
vivify(ref).one.two[3] = 4
|
|
1786
1828
|
|
|
1787
|
-
expect(ref).to.deep.equal(
|
|
1788
|
-
{
|
|
1789
|
-
|
|
1790
|
-
two: [undefined, undefined, undefined, 4],
|
|
1791
|
-
},
|
|
1829
|
+
expect(ref).to.deep.equal({
|
|
1830
|
+
one: {
|
|
1831
|
+
two: [undefined, undefined, undefined, 4],
|
|
1792
1832
|
},
|
|
1793
|
-
)
|
|
1833
|
+
})
|
|
1794
1834
|
|
|
1795
1835
|
expect(vivify(ref).one.two.length).to.equal(4)
|
|
1796
1836
|
|
|
1797
|
-
expect(ref).to.deep.equal(
|
|
1798
|
-
{
|
|
1799
|
-
|
|
1800
|
-
two: [undefined, undefined, undefined, 4],
|
|
1801
|
-
},
|
|
1837
|
+
expect(ref).to.deep.equal({
|
|
1838
|
+
one: {
|
|
1839
|
+
two: [undefined, undefined, undefined, 4],
|
|
1802
1840
|
},
|
|
1803
|
-
)
|
|
1841
|
+
})
|
|
1804
1842
|
|
|
1805
1843
|
vivify(ref).one.two = 3
|
|
1806
1844
|
|
|
@@ -1814,8 +1852,8 @@ delete vivify(ref).one.two
|
|
|
1814
1852
|
|
|
1815
1853
|
expect(ref).to.deep.equal({ one: {} })
|
|
1816
1854
|
|
|
1817
|
-
expect(vivify(ref).one.toString instanceof Function)
|
|
1818
|
-
.
|
|
1855
|
+
expect(vivify(ref).one.toString instanceof Function).to.be
|
|
1856
|
+
.true
|
|
1819
1857
|
|
|
1820
1858
|
expect(vivify(ref).one.toString.name).to.equal('toString')
|
|
1821
1859
|
|
|
@@ -1835,14 +1873,17 @@ expect(ref).to.deep.equal({ one: { two: { three: {} } } })
|
|
|
1835
1873
|
|
|
1836
1874
|
vivify(ref).one.two.three.four = 5
|
|
1837
1875
|
|
|
1838
|
-
expect(ref)
|
|
1839
|
-
|
|
1876
|
+
expect(ref).to.deep.equal({
|
|
1877
|
+
one: { two: { three: { four: 5 } } },
|
|
1878
|
+
})
|
|
1840
1879
|
|
|
1841
|
-
expect(
|
|
1842
|
-
.
|
|
1880
|
+
expect(
|
|
1881
|
+
vivify(ref).one.two.three.four.toString.name
|
|
1882
|
+
).to.equal('toString')
|
|
1843
1883
|
|
|
1844
|
-
expect(ref)
|
|
1845
|
-
|
|
1884
|
+
expect(ref).to.deep.equal({
|
|
1885
|
+
one: { two: { three: { four: 5 } } },
|
|
1886
|
+
})
|
|
1846
1887
|
|
|
1847
1888
|
const u = undefined
|
|
1848
1889
|
|