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