@jackens/nnn 2026.9.28 → 2026.9.30
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/README.md +424 -126
- package/nnn.d.ts +41 -36
- package/nnn.js +18 -8
- package/package.json +6 -5
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# nnn
|
|
2
2
|
|
|
3
|
-
A collection of Jackens’ JavaScript helper utilities (version: `2026.9.
|
|
3
|
+
A collection of Jackens’ JavaScript helper utilities (version: `2026.9.30`).
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -21,10 +21,10 @@ import {
|
|
|
21
21
|
CNode,
|
|
22
22
|
HArgs,
|
|
23
23
|
HArgs1,
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
LexerSpec,
|
|
25
|
+
LexerSpecOptions,
|
|
26
26
|
c,
|
|
27
|
-
|
|
27
|
+
createLexer,
|
|
28
28
|
h,
|
|
29
29
|
monokai,
|
|
30
30
|
nanolightTs,
|
|
@@ -42,26 +42,26 @@ import {
|
|
|
42
42
|
Tuple argument type for the [`h`](#h) and [`s`](#s) helpers.
|
|
43
43
|
- [`HArgs1`](#HArgs1):
|
|
44
44
|
Single argument type for the [`h`](#h) and [`s`](#s) helpers.
|
|
45
|
-
- [`
|
|
46
|
-
A [`
|
|
47
|
-
- [`
|
|
48
|
-
Options of a [`
|
|
45
|
+
- [`LexerSpec`](#LexerSpec):
|
|
46
|
+
A [`createLexer`](#createLexer) spec: a `[metadata, pattern, options?]` tuple.
|
|
47
|
+
- [`LexerSpecOptions`](#LexerSpecOptions):
|
|
48
|
+
Options of a [`createLexer`](#createLexer) spec controlling the lexer state stack.
|
|
49
49
|
- [`c`](#c):
|
|
50
50
|
A minimal CSS-in-JS helper that converts a JavaScript object hierarchy into a CSS string.
|
|
51
|
-
- [`
|
|
52
|
-
A helper for building simple stateful
|
|
51
|
+
- [`createLexer`](#createLexer):
|
|
52
|
+
A helper for building simple stateful lexers (see also [`nanolightTs`](#nanolightTs)).
|
|
53
53
|
- [`h`](#h):
|
|
54
54
|
A lightweight _HyperScript_-style helper for creating and modifying `HTMLElement`s (see also [`s`](#s)).
|
|
55
55
|
- [`monokai`](#monokai):
|
|
56
|
-
A Monokai-inspired color scheme for use with the [`c`](#c) helper and [`nanolightTs`](#nanolightTs)
|
|
56
|
+
A Monokai-inspired color scheme for use with the [`c`](#c) helper and [`nanolightTs`](#nanolightTs) lexer.
|
|
57
57
|
- [`nanolightTs`](#nanolightTs):
|
|
58
|
-
A TypeScript/JavaScript syntax highlighting
|
|
58
|
+
A TypeScript/JavaScript syntax highlighting lexer built using [`createLexer`](#createLexer).
|
|
59
59
|
- [`rwd`](#rwd):
|
|
60
|
-
A responsive web design helper that generates CSS rules for a grid-like layout.
|
|
60
|
+
A responsive web design helper that generates CSS rules for a grid-like layout of floating elements.
|
|
61
61
|
- [`s`](#s):
|
|
62
62
|
A lightweight _HyperScript_-style helper for creating and modifying `SVGElement`s (see also [`h`](#h)).
|
|
63
63
|
- [`svgUse`](#svgUse):
|
|
64
|
-
Shorthand for creating an
|
|
64
|
+
Shorthand for creating an `<svg>` element with a `<use>` child referencing a symbol (e.g. an icon) by ID.
|
|
65
65
|
|
|
66
66
|
### CNode
|
|
67
67
|
|
|
@@ -89,25 +89,25 @@ type HArgs1 = Record<PropertyKey, unknown> | null | undefined | Node | string |
|
|
|
89
89
|
|
|
90
90
|
Single argument type for the [`h`](#h) and [`s`](#s) helpers.
|
|
91
91
|
|
|
92
|
-
###
|
|
92
|
+
### LexerSpec
|
|
93
93
|
|
|
94
94
|
```ts
|
|
95
|
-
type
|
|
95
|
+
type LexerSpec<M> = [metadata: M, pattern: string | RegExp, options?: LexerSpecOptions]
|
|
96
96
|
```
|
|
97
97
|
|
|
98
|
-
A [`
|
|
98
|
+
A [`createLexer`](#createLexer) spec: a `[metadata, pattern, options?]` tuple.
|
|
99
99
|
|
|
100
|
-
###
|
|
100
|
+
### LexerSpecOptions
|
|
101
101
|
|
|
102
102
|
```ts
|
|
103
|
-
type
|
|
103
|
+
type LexerSpecOptions = {
|
|
104
104
|
in?: string | string[]
|
|
105
105
|
pop?: boolean
|
|
106
106
|
push?: string
|
|
107
107
|
}
|
|
108
108
|
```
|
|
109
109
|
|
|
110
|
-
Options of a [`
|
|
110
|
+
Options of a [`createLexer`](#createLexer) spec controlling the lexer state stack.
|
|
111
111
|
|
|
112
112
|
#### in
|
|
113
113
|
|
|
@@ -133,12 +133,15 @@ A minimal CSS-in-JS helper that converts a JavaScript object hierarchy into a CS
|
|
|
133
133
|
|
|
134
134
|
An object describing CSS rules.
|
|
135
135
|
Keys are selectors or at-rules; values are either CSS property values or nested rule objects.
|
|
136
|
+
Nested selectors are appended to the parent selector as is (e.g. `'.c'` inside `a` → `a.c`), so a leading space is needed for a descendant combinator (e.g. `' .c'` → `a .c`).
|
|
136
137
|
A selector key may contain comma-separated alternatives (e.g. `'.b,.c'`); when nested, each alternative is combined with each parent alternative (cartesian product), producing a comma-joined selector list (e.g. `'a.b,a.c'`).
|
|
138
|
+
Top-level at-rules (e.g. `'@media …'`) wrap their nested rules; inside a top-level `:root` rule nested keys are concatenated into property names (e.g. `{ ':root': { '--': { bg: 'red' } } }` → `:root{--bg:red}`).
|
|
139
|
+
`null`/`undefined` values are skipped.
|
|
137
140
|
|
|
138
141
|
#### splitter
|
|
139
142
|
|
|
140
143
|
A delimiter used to create unique keys (default: `'$$'`).
|
|
141
|
-
The substring from `splitter` to the end of a key is ignored (e.g
|
|
144
|
+
The substring from `splitter` to the end of a key is ignored (e.g. `src$$1` → `src`).
|
|
142
145
|
|
|
143
146
|
#### Returns
|
|
144
147
|
|
|
@@ -146,6 +149,8 @@ A CSS string representing the compiled rules.
|
|
|
146
149
|
|
|
147
150
|
#### Usage Examples
|
|
148
151
|
|
|
152
|
+
Nested selectors split a rule while preserving property order:
|
|
153
|
+
|
|
149
154
|
```ts
|
|
150
155
|
const actual = c({
|
|
151
156
|
a: {
|
|
@@ -173,6 +178,8 @@ const expected = `
|
|
|
173
178
|
expect(actual).toBe(expected)
|
|
174
179
|
```
|
|
175
180
|
|
|
181
|
+
Selectors can be nested at any depth:
|
|
182
|
+
|
|
176
183
|
```ts
|
|
177
184
|
const actual = c({
|
|
178
185
|
a: {
|
|
@@ -202,6 +209,8 @@ const expected = `
|
|
|
202
209
|
expect(actual).toBe(expected)
|
|
203
210
|
```
|
|
204
211
|
|
|
212
|
+
At-rules, keyframes, media queries and repeated keys with the `$$` suffix:
|
|
213
|
+
|
|
205
214
|
```ts
|
|
206
215
|
const actual = c({
|
|
207
216
|
'@font-face$$1': {
|
|
@@ -277,6 +286,8 @@ const expected = `
|
|
|
277
286
|
expect(actual).toBe(expected)
|
|
278
287
|
```
|
|
279
288
|
|
|
289
|
+
Comma-separated nested selectors are expanded:
|
|
290
|
+
|
|
280
291
|
```ts
|
|
281
292
|
const actual = c({
|
|
282
293
|
a: {
|
|
@@ -301,6 +312,8 @@ const expected = `
|
|
|
301
312
|
expect(actual).toBe(expected)
|
|
302
313
|
```
|
|
303
314
|
|
|
315
|
+
Comma-separated top-level selectors are expanded in nested rules:
|
|
316
|
+
|
|
304
317
|
```ts
|
|
305
318
|
const actual = c({
|
|
306
319
|
'.b,.c': {
|
|
@@ -323,6 +336,8 @@ const expected = `
|
|
|
323
336
|
expect(actual).toBe(expected)
|
|
324
337
|
```
|
|
325
338
|
|
|
339
|
+
Comma-separated selectors at both levels produce all combinations:
|
|
340
|
+
|
|
326
341
|
```ts
|
|
327
342
|
const actual = c({
|
|
328
343
|
'.a,.b': {
|
|
@@ -345,6 +360,8 @@ const expected = `
|
|
|
345
360
|
expect(actual).toBe(expected)
|
|
346
361
|
```
|
|
347
362
|
|
|
363
|
+
CSS custom properties from nested `--` objects:
|
|
364
|
+
|
|
348
365
|
```ts
|
|
349
366
|
const actual = c({
|
|
350
367
|
':root$$monokai': {
|
|
@@ -435,16 +452,16 @@ const expected = `
|
|
|
435
452
|
expect(actual).toBe(expected)
|
|
436
453
|
```
|
|
437
454
|
|
|
438
|
-
###
|
|
455
|
+
### createLexer
|
|
439
456
|
|
|
440
457
|
```ts
|
|
441
|
-
const
|
|
458
|
+
const createLexer: <M, T>(
|
|
442
459
|
decorator: (chunk: string, metadata?: M) => T,
|
|
443
|
-
...specs:
|
|
460
|
+
...specs: LexerSpec<M>[]
|
|
444
461
|
) => (code: string) => T[]
|
|
445
462
|
```
|
|
446
463
|
|
|
447
|
-
A helper for building simple stateful
|
|
464
|
+
A helper for building simple stateful lexers (see also [`nanolightTs`](#nanolightTs)).
|
|
448
465
|
|
|
449
466
|
#### Remarks
|
|
450
467
|
|
|
@@ -457,32 +474,34 @@ A helper for building simple stateful tokenizers (see also [`nanolightTs`](#nano
|
|
|
457
474
|
|
|
458
475
|
#### decorator
|
|
459
476
|
|
|
460
|
-
A function that wraps each
|
|
461
|
-
It receives the matched text (`chunk`) and
|
|
462
|
-
For unmatched text between
|
|
477
|
+
A function that wraps each chunk of the input.
|
|
478
|
+
It receives the matched text (`chunk`) and the `metadata` of the spec whose pattern produced the match.
|
|
479
|
+
For unmatched text between matches, `metadata` is `undefined`.
|
|
463
480
|
|
|
464
481
|
#### specs
|
|
465
482
|
|
|
466
483
|
An array of tuples `[metadata, pattern, options?]` where:
|
|
467
484
|
|
|
468
485
|
- `metadata`: arbitrary data (e.g., a CSS class name) passed to `decorator` when the pattern matches.
|
|
469
|
-
- `pattern`: a `string` or `RegExp` to match against the input.
|
|
470
|
-
- `options`: optional [`
|
|
486
|
+
- `pattern`: a `string` (matched literally) or `RegExp` to match against the input.
|
|
487
|
+
- `options`: optional [`LexerSpecOptions`](#LexerSpecOptions) (states in which the spec is active and state transitions).
|
|
471
488
|
|
|
472
489
|
#### Returns
|
|
473
490
|
|
|
474
|
-
A
|
|
491
|
+
A lexer function that accepts an input string and returns an array of decorated chunks covering the whole input (in order).
|
|
475
492
|
|
|
476
493
|
#### Usage Examples
|
|
477
494
|
|
|
495
|
+
Tokenizing keywords and strings with `RegExp` patterns:
|
|
496
|
+
|
|
478
497
|
```ts
|
|
479
|
-
const
|
|
498
|
+
const lexer = createLexer(
|
|
480
499
|
(chunk, metadata) => ({ chunk, metadata }),
|
|
481
500
|
['keyword', /\b(if|else|for)\b/],
|
|
482
501
|
['string', /"[^"]*"/]
|
|
483
502
|
)
|
|
484
503
|
|
|
485
|
-
const result =
|
|
504
|
+
const result = lexer('if "hello" else "world"')
|
|
486
505
|
|
|
487
506
|
expect(result).toEqual([
|
|
488
507
|
{ chunk: 'if', metadata: 'keyword' },
|
|
@@ -495,26 +514,32 @@ expect(result).toEqual([
|
|
|
495
514
|
])
|
|
496
515
|
```
|
|
497
516
|
|
|
517
|
+
String patterns are matched literally and unmatched text gets `undefined` metadata:
|
|
518
|
+
|
|
498
519
|
```ts
|
|
499
|
-
const
|
|
520
|
+
const lexer = createLexer((chunk, metadata) => `${metadata}:${chunk}`, ['tag', 'BEGIN'], ['end', 'END'])
|
|
500
521
|
|
|
501
|
-
const result =
|
|
522
|
+
const result = lexer('aBEGINbENDc')
|
|
502
523
|
|
|
503
524
|
expect(result).toEqual(['undefined:a', 'tag:BEGIN', 'undefined:b', 'end:END', 'undefined:c'])
|
|
504
525
|
```
|
|
505
526
|
|
|
527
|
+
Empty input produces an empty result:
|
|
528
|
+
|
|
506
529
|
```ts
|
|
507
|
-
const
|
|
530
|
+
const lexer = createLexer(chunk => chunk, ['test', /test/])
|
|
508
531
|
|
|
509
|
-
const result =
|
|
532
|
+
const result = lexer('')
|
|
510
533
|
|
|
511
534
|
expect(result).toEqual([])
|
|
512
535
|
```
|
|
513
536
|
|
|
537
|
+
`^` anchors to the start of the whole input:
|
|
538
|
+
|
|
514
539
|
```ts
|
|
515
|
-
const
|
|
540
|
+
const lexer = createLexer((chunk, metadata) => ({ chunk, metadata }), ['start', /^test/])
|
|
516
541
|
|
|
517
|
-
const result =
|
|
542
|
+
const result = lexer('test here')
|
|
518
543
|
|
|
519
544
|
expect(result).toEqual([
|
|
520
545
|
{ chunk: 'test', metadata: 'start' },
|
|
@@ -522,91 +547,111 @@ expect(result).toEqual([
|
|
|
522
547
|
])
|
|
523
548
|
```
|
|
524
549
|
|
|
550
|
+
Matches starting earlier take precedence regardless of spec order:
|
|
551
|
+
|
|
525
552
|
```ts
|
|
526
|
-
const
|
|
553
|
+
const lexer = createLexer((chunk, metadata) => metadata, ['later', /x/], ['earlier', /y/])
|
|
527
554
|
|
|
528
|
-
const result =
|
|
555
|
+
const result = lexer('yx')
|
|
529
556
|
|
|
530
557
|
expect(result).toEqual(['earlier', 'later'])
|
|
531
558
|
```
|
|
532
559
|
|
|
560
|
+
The longer match wins at the same position:
|
|
561
|
+
|
|
533
562
|
```ts
|
|
534
|
-
const
|
|
563
|
+
const lexer = createLexer(chunk => chunk, ['short', 'a'], ['long', 'abc'])
|
|
535
564
|
|
|
536
|
-
const result =
|
|
565
|
+
const result = lexer('abc')
|
|
537
566
|
|
|
538
567
|
expect(result).toEqual(['abc'])
|
|
539
568
|
```
|
|
540
569
|
|
|
570
|
+
Empty string patterns are ignored:
|
|
571
|
+
|
|
541
572
|
```ts
|
|
542
|
-
const
|
|
573
|
+
const lexer = createLexer(chunk => chunk, ['empty', ''], ['word', /\w+/])
|
|
543
574
|
|
|
544
|
-
const result =
|
|
575
|
+
const result = lexer('hello')
|
|
545
576
|
|
|
546
577
|
expect(result).toEqual(['hello'])
|
|
547
578
|
```
|
|
548
579
|
|
|
580
|
+
Input without any match is returned as a single chunk:
|
|
581
|
+
|
|
549
582
|
```ts
|
|
550
|
-
const
|
|
583
|
+
const lexer = createLexer(chunk => chunk, ['test', /xyz/])
|
|
551
584
|
|
|
552
|
-
const result =
|
|
585
|
+
const result = lexer('abc')
|
|
553
586
|
|
|
554
587
|
expect(result).toEqual(['abc'])
|
|
555
588
|
```
|
|
556
589
|
|
|
590
|
+
Consecutive matches are returned as separate chunks:
|
|
591
|
+
|
|
557
592
|
```ts
|
|
558
|
-
const
|
|
593
|
+
const lexer = createLexer((_chunk, metadata) => metadata, ['a', 'a'], ['b', 'b'])
|
|
559
594
|
|
|
560
|
-
const result =
|
|
595
|
+
const result = lexer('aabb')
|
|
561
596
|
|
|
562
597
|
expect(result).toEqual(['a', 'a', 'b', 'b'])
|
|
563
598
|
```
|
|
564
599
|
|
|
600
|
+
Entering and leaving states with `push` and `pop`:
|
|
601
|
+
|
|
565
602
|
```ts
|
|
566
|
-
const
|
|
603
|
+
const lexer = createLexer(
|
|
567
604
|
(chunk, metadata) => `${metadata}:${chunk}`,
|
|
568
605
|
['open', '(', { push: 'inner' }],
|
|
569
606
|
['word', /\w+/, { in: 'inner' }],
|
|
570
607
|
['close', ')', { in: 'inner', pop: true }]
|
|
571
608
|
)
|
|
572
609
|
|
|
573
|
-
const result =
|
|
610
|
+
const result = lexer('a(b)c)')
|
|
574
611
|
|
|
575
612
|
expect(result).toEqual(['undefined:a', 'open:(', 'word:b', 'close:)', 'undefined:c)'])
|
|
576
613
|
```
|
|
577
614
|
|
|
615
|
+
Nested states with a spec active in multiple states:
|
|
616
|
+
|
|
578
617
|
```ts
|
|
579
|
-
const
|
|
618
|
+
const lexer = createLexer(
|
|
580
619
|
(chunk, metadata) => `${metadata}:${chunk}`,
|
|
581
620
|
['open', '{', { in: ['root', 'nested'], push: 'nested' }],
|
|
582
621
|
['close', '}', { in: 'nested', pop: true }]
|
|
583
622
|
)
|
|
584
623
|
|
|
585
|
-
const result =
|
|
624
|
+
const result = lexer('{{}}}')
|
|
586
625
|
|
|
587
626
|
expect(result).toEqual(['open:{', 'open:{', 'close:}', 'close:}', 'undefined:}'])
|
|
588
627
|
```
|
|
589
628
|
|
|
629
|
+
Lookbehinds see the text preceding the current position:
|
|
630
|
+
|
|
590
631
|
```ts
|
|
591
|
-
const
|
|
632
|
+
const lexer = createLexer((chunk, metadata) => `${metadata}:${chunk}`, ['a', 'a'], ['b', /(?<=a)b/])
|
|
592
633
|
|
|
593
|
-
const result =
|
|
634
|
+
const result = lexer('abcb')
|
|
594
635
|
|
|
595
636
|
expect(result).toEqual(['a:a', 'b:b', 'undefined:cb'])
|
|
596
637
|
```
|
|
597
638
|
|
|
639
|
+
Sticky patterns match only at the current position:
|
|
640
|
+
|
|
598
641
|
```ts
|
|
599
|
-
const
|
|
642
|
+
const lexer = createLexer((chunk, metadata) => `${metadata}:${chunk}`, ['x', /x/y], ['y', 'y'])
|
|
600
643
|
|
|
601
|
-
const result =
|
|
644
|
+
const result = lexer('xyx.x')
|
|
602
645
|
|
|
603
646
|
expect(result).toEqual(['x:x', 'y:y', 'x:x', 'undefined:.x'])
|
|
604
647
|
```
|
|
605
648
|
|
|
649
|
+
Empty `RegExp` matches are skipped:
|
|
650
|
+
|
|
606
651
|
```ts
|
|
607
|
-
const
|
|
652
|
+
const lexer = createLexer((chunk, metadata) => `${metadata}:${chunk}`, ['digits', /\d*/])
|
|
608
653
|
|
|
609
|
-
const result =
|
|
654
|
+
const result = lexer('ab12')
|
|
610
655
|
|
|
611
656
|
expect(result).toEqual(['undefined:ab', 'digits:12'])
|
|
612
657
|
```
|
|
@@ -615,9 +660,9 @@ expect(result).toEqual(['undefined:ab', 'digits:12'])
|
|
|
615
660
|
|
|
616
661
|
```ts
|
|
617
662
|
const h: {
|
|
618
|
-
<T extends keyof HTMLElementTagNameMap>(tag: T, ...
|
|
619
|
-
<N extends Node>(node: N, ...
|
|
620
|
-
(tagOrNode: string | Node, ...
|
|
663
|
+
<T extends keyof HTMLElementTagNameMap>(tag: T, ...args: HArgs1[]): HTMLElementTagNameMap[T]
|
|
664
|
+
<N extends Node>(node: N, ...args: HArgs1[]): N
|
|
665
|
+
(tagOrNode: string | Node, ...args: HArgs1[]): Node
|
|
621
666
|
}
|
|
622
667
|
```
|
|
623
668
|
|
|
@@ -633,20 +678,22 @@ If a `Node`, that node is modified in place.
|
|
|
633
678
|
Additional arguments processed as follows:
|
|
634
679
|
|
|
635
680
|
- `Object`: maps attributes/properties.
|
|
636
|
-
Keys starting with `$` set element properties (without the `$` prefix); if the value is a plain object, it is shallow-merged into the existing property instead of replacing it (useful e.g. for `$style`).
|
|
637
|
-
Other keys set attributes: a key prefixed with `xlink:` sets the attribute via `setAttributeNS` in the XLink namespace; any other key (without a colon) sets it via `setAttribute`.
|
|
638
|
-
|
|
681
|
+
- Keys starting with `$` set element properties (without the `$` prefix); if the value is a plain object, it is shallow-merged into the existing property instead of replacing it (useful e.g. for `$style`).
|
|
682
|
+
- Other keys set attributes: a key prefixed with `xlink:` sets the attribute via `setAttributeNS` in the XLink namespace; any other key (without a colon) sets it via `setAttribute`.
|
|
683
|
+
A value of `true` sets an empty attribute (e.g. `checked=""`), a value of `false` removes the attribute.
|
|
639
684
|
- `null`/`undefined`: ignored.
|
|
640
685
|
- `Node`: appended as a child.
|
|
641
686
|
- `string`/`number`: converted to a `Text` node and appended.
|
|
642
|
-
- [`HArgs`](#HArgs) array: processed recursively.
|
|
687
|
+
- [`HArgs`](#HArgs) array: processed recursively (creates or modifies a child node and appends it).
|
|
643
688
|
|
|
644
689
|
#### Returns
|
|
645
690
|
|
|
646
|
-
The created or modified
|
|
691
|
+
The created `HTMLElement` or the modified node.
|
|
647
692
|
|
|
648
693
|
#### Usage Examples
|
|
649
694
|
|
|
695
|
+
Creating elements, appending children and setting properties:
|
|
696
|
+
|
|
650
697
|
```ts
|
|
651
698
|
const b = h('b')
|
|
652
699
|
|
|
@@ -667,6 +714,8 @@ expect(i.outerHTML).toBe('<i class="some class">text</i>')
|
|
|
667
714
|
expect(b.outerHTML).toBe('<b><i class="some class">text</i></b>')
|
|
668
715
|
```
|
|
669
716
|
|
|
717
|
+
Text content from strings, numbers and `$innerText`:
|
|
718
|
+
|
|
670
719
|
```ts
|
|
671
720
|
expect(h('span', 'text').outerHTML).toBe('<span>text</span>')
|
|
672
721
|
|
|
@@ -677,6 +726,8 @@ expect(h('span', '42').outerHTML).toBe('<span>42</span>')
|
|
|
677
726
|
expect(h('span', 42).outerHTML).toBe('<span>42</span>')
|
|
678
727
|
```
|
|
679
728
|
|
|
729
|
+
`style` attribute vs `$style` property (string or object):
|
|
730
|
+
|
|
680
731
|
```ts
|
|
681
732
|
expect(h('div', { style: 'margin:0;padding:0' }).outerHTML).toBe('<div style="margin:0;padding:0"></div>')
|
|
682
733
|
|
|
@@ -687,6 +738,8 @@ expect(h('div', { $style: { margin: 0, padding: 0 } }).outerHTML).toBe(
|
|
|
687
738
|
)
|
|
688
739
|
```
|
|
689
740
|
|
|
741
|
+
`value` attribute vs `$value` property:
|
|
742
|
+
|
|
690
743
|
```ts
|
|
691
744
|
const input1 = h('input', { value: 42 })
|
|
692
745
|
const input2 = h('input', { $value: '42' })
|
|
@@ -698,6 +751,8 @@ expect(input1.outerHTML).toBe('<input value="42">')
|
|
|
698
751
|
expect(input2.outerHTML).toBe('<input>')
|
|
699
752
|
```
|
|
700
753
|
|
|
754
|
+
`checked` attribute vs `$checked` property:
|
|
755
|
+
|
|
701
756
|
```ts
|
|
702
757
|
const checkbox1 = h('input', {
|
|
703
758
|
type: 'checkbox',
|
|
@@ -717,6 +772,8 @@ expect(checkbox1.outerHTML).toBe('<input type="checkbox" checked="">')
|
|
|
717
772
|
expect(checkbox2.outerHTML).toBe('<input type="checkbox">')
|
|
718
773
|
```
|
|
719
774
|
|
|
775
|
+
Object values of `$`-prefixed properties are merged:
|
|
776
|
+
|
|
720
777
|
```ts
|
|
721
778
|
const div = h('div')
|
|
722
779
|
|
|
@@ -731,6 +788,8 @@ h(div, { $key: { two: 2 } })
|
|
|
731
788
|
expect(div.key).toEqual({ one: 1, two: 2 })
|
|
732
789
|
```
|
|
733
790
|
|
|
791
|
+
Class attribute, appending text and nested children given as arrays:
|
|
792
|
+
|
|
734
793
|
```ts
|
|
735
794
|
const elemWithClass = h('div', { class: 'test' })
|
|
736
795
|
|
|
@@ -753,7 +812,29 @@ expect(elemWithNested.outerHTML).toBe('<div><span>hello</span><b>world</b></div>
|
|
|
753
812
|
const monokai: CNode
|
|
754
813
|
```
|
|
755
814
|
|
|
756
|
-
A Monokai-inspired color scheme for use with the [`c`](#c) helper and [`nanolightTs`](#nanolightTs)
|
|
815
|
+
A Monokai-inspired color scheme for use with the [`c`](#c) helper and [`nanolightTs`](#nanolightTs) lexer.
|
|
816
|
+
It styles `pre` and `code>span` elements with the class names produced by [`nanolightTs`](#nanolightTs) (with light, dark and print variants).
|
|
817
|
+
|
|
818
|
+
#### Usage Examples
|
|
819
|
+
|
|
820
|
+
Styling `nanolightTs` output:
|
|
821
|
+
|
|
822
|
+
```ts
|
|
823
|
+
const style = h('style', c(monokai))
|
|
824
|
+
const pre = h('pre', ['code', ...nanolightTs('const x = 42')])
|
|
825
|
+
|
|
826
|
+
expect(style.textContent).toContain('code>span.keyword1{color:var(--keyword1)}')
|
|
827
|
+
expect(style.textContent).toContain('code>span.regexp{color:var(--regexp)}')
|
|
828
|
+
expect(style.textContent).toContain('@media only screen and (prefers-color-scheme: dark){:root{--bg:#2d2a2e;')
|
|
829
|
+
expect(pre.outerHTML).toBe(
|
|
830
|
+
'<pre><code>' +
|
|
831
|
+
'<span class="keyword1">const</span> ' +
|
|
832
|
+
'<span class="identifier4">x</span> ' +
|
|
833
|
+
'<span class="operator">=</span> ' +
|
|
834
|
+
'<span class="number">42</span>' +
|
|
835
|
+
'</code></pre>'
|
|
836
|
+
)
|
|
837
|
+
```
|
|
757
838
|
|
|
758
839
|
### nanolightTs
|
|
759
840
|
|
|
@@ -761,7 +842,7 @@ A Monokai-inspired color scheme for use with the [`c`](#c) helper and [`nanoligh
|
|
|
761
842
|
const nanolightTs: (code: string) => HArgs1[]
|
|
762
843
|
```
|
|
763
844
|
|
|
764
|
-
A TypeScript/JavaScript syntax highlighting
|
|
845
|
+
A TypeScript/JavaScript syntax highlighting lexer built using [`createLexer`](#createLexer).
|
|
765
846
|
|
|
766
847
|
#### code
|
|
767
848
|
|
|
@@ -769,10 +850,12 @@ The source code string to tokenize.
|
|
|
769
850
|
|
|
770
851
|
#### Returns
|
|
771
852
|
|
|
772
|
-
An array of [`HArgs1`](#HArgs1) elements suitable for rendering with [`h`](#h).
|
|
853
|
+
An array of [`HArgs1`](#HArgs1) elements suitable for rendering with [`h`](#h): plain `string`s for unrecognized text (e.g. whitespace) and `['span', { class }, chunk]` tuples for recognized tokens (class names are styled by [`monokai`](#monokai)).
|
|
773
854
|
|
|
774
855
|
#### Usage Examples
|
|
775
856
|
|
|
857
|
+
Keywords, identifiers, operators, numbers, punctuation, strings and comments:
|
|
858
|
+
|
|
776
859
|
```ts
|
|
777
860
|
const codeJs = 'const answerToLifeTheUniverseAndEverything = ' + "{ 42: 42 }['42'] /* 42 */"
|
|
778
861
|
|
|
@@ -799,78 +882,280 @@ expect(nanolightTs(codeJs)).toEqual([
|
|
|
799
882
|
])
|
|
800
883
|
```
|
|
801
884
|
|
|
885
|
+
Line comments:
|
|
886
|
+
|
|
887
|
+
```ts
|
|
888
|
+
const codeJs = 'const x = 42 // answer'
|
|
889
|
+
|
|
890
|
+
expect(nanolightTs(codeJs)).toEqual([
|
|
891
|
+
['span', { class: 'keyword1' }, 'const'],
|
|
892
|
+
' ',
|
|
893
|
+
['span', { class: 'identifier4' }, 'x'],
|
|
894
|
+
' ',
|
|
895
|
+
['span', { class: 'operator' }, '='],
|
|
896
|
+
' ',
|
|
897
|
+
['span', { class: 'number' }, '42'],
|
|
898
|
+
' ',
|
|
899
|
+
['span', { class: 'comment' }, '// answer'],
|
|
900
|
+
])
|
|
901
|
+
```
|
|
902
|
+
|
|
903
|
+
Regex literals vs division:
|
|
904
|
+
|
|
905
|
+
```ts
|
|
906
|
+
expect(nanolightTs('x = /a\\/b[/]c/gi.test(y) / 2')).toEqual([
|
|
907
|
+
['span', { class: 'identifier4' }, 'x'],
|
|
908
|
+
' ',
|
|
909
|
+
['span', { class: 'operator' }, '='],
|
|
910
|
+
' ',
|
|
911
|
+
['span', { class: 'regexp' }, '/'],
|
|
912
|
+
['span', { class: 'regexp' }, 'a'],
|
|
913
|
+
['span', { class: 'keyword2' }, '\\/'],
|
|
914
|
+
['span', { class: 'regexp' }, 'b'],
|
|
915
|
+
['span', { class: 'punctuation' }, '['],
|
|
916
|
+
['span', { class: 'regexp' }, '/'],
|
|
917
|
+
['span', { class: 'punctuation' }, ']'],
|
|
918
|
+
['span', { class: 'regexp' }, 'c'],
|
|
919
|
+
['span', { class: 'regexp' }, '/'],
|
|
920
|
+
['span', { class: 'keyword1' }, 'gi'],
|
|
921
|
+
['span', { class: 'punctuation' }, '.'],
|
|
922
|
+
['span', { class: 'identifier2' }, 'test'],
|
|
923
|
+
['span', { class: 'punctuation' }, '('],
|
|
924
|
+
['span', { class: 'identifier4' }, 'y'],
|
|
925
|
+
['span', { class: 'punctuation' }, ')'],
|
|
926
|
+
' ',
|
|
927
|
+
['span', { class: 'operator' }, '/'],
|
|
928
|
+
' ',
|
|
929
|
+
['span', { class: 'number' }, '2'],
|
|
930
|
+
])
|
|
931
|
+
|
|
932
|
+
expect(nanolightTs('a / b / c')).toEqual([
|
|
933
|
+
['span', { class: 'identifier4' }, 'a'],
|
|
934
|
+
' ',
|
|
935
|
+
['span', { class: 'operator' }, '/'],
|
|
936
|
+
' ',
|
|
937
|
+
['span', { class: 'identifier4' }, 'b'],
|
|
938
|
+
' ',
|
|
939
|
+
['span', { class: 'operator' }, '/'],
|
|
940
|
+
' ',
|
|
941
|
+
['span', { class: 'identifier4' }, 'c'],
|
|
942
|
+
])
|
|
943
|
+
|
|
944
|
+
expect(nanolightTs('return / x /')).toEqual([
|
|
945
|
+
['span', { class: 'keyword1' }, 'return'],
|
|
946
|
+
' ',
|
|
947
|
+
['span', { class: 'regexp' }, '/'],
|
|
948
|
+
['span', { class: 'regexp' }, ' x '],
|
|
949
|
+
['span', { class: 'regexp' }, '/'],
|
|
950
|
+
])
|
|
951
|
+
|
|
952
|
+
expect(nanolightTs('x = /=/g')).toEqual([
|
|
953
|
+
['span', { class: 'identifier4' }, 'x'],
|
|
954
|
+
' ',
|
|
955
|
+
['span', { class: 'operator' }, '='],
|
|
956
|
+
' ',
|
|
957
|
+
['span', { class: 'regexp' }, '/='],
|
|
958
|
+
['span', { class: 'regexp' }, '/'],
|
|
959
|
+
['span', { class: 'keyword1' }, 'g'],
|
|
960
|
+
])
|
|
961
|
+
```
|
|
962
|
+
|
|
963
|
+
Nested template literals with substitutions:
|
|
964
|
+
|
|
802
965
|
```ts
|
|
803
|
-
expect(
|
|
804
|
-
'
|
|
966
|
+
expect(nanolightTs('`a ${b + `c ${ {d}.d }`} \\${e} $f`')).toEqual([
|
|
967
|
+
['span', { class: 'string' }, '`a '],
|
|
968
|
+
['span', { class: 'punctuation' }, '${'],
|
|
969
|
+
['span', { class: 'identifier4' }, 'b'],
|
|
805
970
|
' ',
|
|
806
|
-
'operator
|
|
971
|
+
['span', { class: 'operator' }, '+'],
|
|
807
972
|
' ',
|
|
808
|
-
'string
|
|
809
|
-
'punctuation
|
|
810
|
-
'identifier2:test',
|
|
811
|
-
'punctuation:(',
|
|
812
|
-
'identifier4:y',
|
|
813
|
-
'punctuation:)',
|
|
973
|
+
['span', { class: 'string' }, '`c '],
|
|
974
|
+
['span', { class: 'punctuation' }, '${'],
|
|
814
975
|
' ',
|
|
815
|
-
'
|
|
976
|
+
['span', { class: 'punctuation' }, '{'],
|
|
977
|
+
['span', { class: 'identifier4' }, 'd'],
|
|
978
|
+
['span', { class: 'punctuation' }, '}'],
|
|
979
|
+
['span', { class: 'punctuation' }, '.'],
|
|
980
|
+
['span', { class: 'identifier4' }, 'd'],
|
|
816
981
|
' ',
|
|
817
|
-
'
|
|
982
|
+
['span', { class: 'punctuation' }, '}'],
|
|
983
|
+
['span', { class: 'string' }, '`'],
|
|
984
|
+
['span', { class: 'punctuation' }, '}'],
|
|
985
|
+
['span', { class: 'string' }, ' \\${e} $f`'],
|
|
818
986
|
])
|
|
819
987
|
|
|
820
|
-
expect(
|
|
821
|
-
'
|
|
988
|
+
expect(nanolightTs('`${x}` / 2')).toEqual([
|
|
989
|
+
['span', { class: 'string' }, '`'],
|
|
990
|
+
['span', { class: 'punctuation' }, '${'],
|
|
991
|
+
['span', { class: 'identifier4' }, 'x'],
|
|
992
|
+
['span', { class: 'punctuation' }, '}'],
|
|
993
|
+
['span', { class: 'string' }, '`'],
|
|
822
994
|
' ',
|
|
823
|
-
'operator
|
|
995
|
+
['span', { class: 'operator' }, '/'],
|
|
824
996
|
' ',
|
|
825
|
-
'
|
|
997
|
+
['span', { class: 'number' }, '2'],
|
|
998
|
+
])
|
|
999
|
+
```
|
|
1000
|
+
|
|
1001
|
+
Comment markers inside strings and unterminated comments:
|
|
1002
|
+
|
|
1003
|
+
```ts
|
|
1004
|
+
expect(nanolightTs('"a // b" // c')).toEqual([
|
|
1005
|
+
['span', { class: 'string' }, '"a // b"'],
|
|
826
1006
|
' ',
|
|
827
|
-
'
|
|
1007
|
+
['span', { class: 'comment' }, '// c'],
|
|
1008
|
+
])
|
|
1009
|
+
|
|
1010
|
+
expect(nanolightTs("'it\\'s' /* x")).toEqual([
|
|
1011
|
+
['span', { class: 'string' }, "'it\\'s'"],
|
|
828
1012
|
' ',
|
|
829
|
-
'
|
|
1013
|
+
['span', { class: 'comment' }, '/* x'],
|
|
830
1014
|
])
|
|
1015
|
+
```
|
|
1016
|
+
|
|
1017
|
+
Hexadecimal, BigInt, numeric separators and exponents:
|
|
831
1018
|
|
|
832
|
-
|
|
1019
|
+
```ts
|
|
1020
|
+
expect(nanolightTs('0xFF + 1_000n + .5e-3')).toEqual([
|
|
1021
|
+
['span', { class: 'number' }, '0xFF'],
|
|
1022
|
+
' ',
|
|
1023
|
+
['span', { class: 'operator' }, '+'],
|
|
1024
|
+
' ',
|
|
1025
|
+
['span', { class: 'number' }, '1_000n'],
|
|
1026
|
+
' ',
|
|
1027
|
+
['span', { class: 'operator' }, '+'],
|
|
1028
|
+
' ',
|
|
1029
|
+
['span', { class: 'number' }, '.5e-3'],
|
|
1030
|
+
])
|
|
833
1031
|
```
|
|
834
1032
|
|
|
1033
|
+
Division after `++` and optional chaining vs ternary with decimals:
|
|
1034
|
+
|
|
835
1035
|
```ts
|
|
836
|
-
expect(
|
|
837
|
-
'
|
|
838
|
-
'
|
|
839
|
-
'
|
|
1036
|
+
expect(nanolightTs('a++ / 2 / b')).toEqual([
|
|
1037
|
+
['span', { class: 'identifier4' }, 'a'],
|
|
1038
|
+
['span', { class: 'operator' }, '++'],
|
|
1039
|
+
' ',
|
|
1040
|
+
['span', { class: 'operator' }, '/'],
|
|
840
1041
|
' ',
|
|
841
|
-
'
|
|
1042
|
+
['span', { class: 'number' }, '2'],
|
|
842
1043
|
' ',
|
|
843
|
-
'
|
|
844
|
-
'punctuation:${',
|
|
1044
|
+
['span', { class: 'operator' }, '/'],
|
|
845
1045
|
' ',
|
|
846
|
-
'
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
'
|
|
1046
|
+
['span', { class: 'identifier4' }, 'b'],
|
|
1047
|
+
])
|
|
1048
|
+
|
|
1049
|
+
expect(nanolightTs('x?.5:a?.b')).toEqual([
|
|
1050
|
+
['span', { class: 'identifier4' }, 'x'],
|
|
1051
|
+
['span', { class: 'operator' }, '?'],
|
|
1052
|
+
['span', { class: 'number' }, '.5'],
|
|
1053
|
+
['span', { class: 'operator' }, ':'],
|
|
1054
|
+
['span', { class: 'identifier4' }, 'a'],
|
|
1055
|
+
['span', { class: 'punctuation' }, '?.'],
|
|
1056
|
+
['span', { class: 'identifier4' }, 'b'],
|
|
1057
|
+
])
|
|
1058
|
+
```
|
|
1059
|
+
|
|
1060
|
+
Keywords as property names and method calls:
|
|
1061
|
+
|
|
1062
|
+
```ts
|
|
1063
|
+
expect(nanolightTs('p.catch(obj.in, ...this)')).toEqual([
|
|
1064
|
+
['span', { class: 'identifier4' }, 'p'],
|
|
1065
|
+
['span', { class: 'punctuation' }, '.'],
|
|
1066
|
+
['span', { class: 'identifier2' }, 'catch'],
|
|
1067
|
+
['span', { class: 'punctuation' }, '('],
|
|
1068
|
+
['span', { class: 'identifier4' }, 'obj'],
|
|
1069
|
+
['span', { class: 'punctuation' }, '.'],
|
|
1070
|
+
['span', { class: 'identifier4' }, 'in'],
|
|
1071
|
+
['span', { class: 'punctuation' }, ','],
|
|
851
1072
|
' ',
|
|
852
|
-
'
|
|
853
|
-
'
|
|
854
|
-
'punctuation
|
|
855
|
-
'string: \\${e} $f`',
|
|
1073
|
+
['span', { class: 'operator' }, '...'],
|
|
1074
|
+
['span', { class: 'keyword1' }, 'this'],
|
|
1075
|
+
['span', { class: 'punctuation' }, ')'],
|
|
856
1076
|
])
|
|
1077
|
+
```
|
|
857
1078
|
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
'
|
|
863
|
-
'
|
|
1079
|
+
Hashbang, decorators and private class members:
|
|
1080
|
+
|
|
1081
|
+
```ts
|
|
1082
|
+
expect(nanolightTs('#!/usr/bin/env bun\n@sealed class A { #x = 1 }')).toEqual([
|
|
1083
|
+
['span', { class: 'comment' }, '#!/usr/bin/env bun'],
|
|
1084
|
+
'\n',
|
|
1085
|
+
['span', { class: 'identifier2' }, '@sealed'],
|
|
1086
|
+
' ',
|
|
1087
|
+
['span', { class: 'keyword1' }, 'class'],
|
|
1088
|
+
' ',
|
|
1089
|
+
['span', { class: 'identifier1' }, 'A'],
|
|
864
1090
|
' ',
|
|
865
|
-
'
|
|
1091
|
+
['span', { class: 'punctuation' }, '{'],
|
|
1092
|
+
' ',
|
|
1093
|
+
['span', { class: 'identifier4' }, '#x'],
|
|
1094
|
+
' ',
|
|
1095
|
+
['span', { class: 'operator' }, '='],
|
|
866
1096
|
' ',
|
|
867
|
-
'
|
|
1097
|
+
['span', { class: 'number' }, '1'],
|
|
1098
|
+
' ',
|
|
1099
|
+
['span', { class: 'punctuation' }, '}'],
|
|
868
1100
|
])
|
|
869
1101
|
```
|
|
870
1102
|
|
|
1103
|
+
Escapes, anchors, quantifiers, groups, character sets and flags inside regex literals:
|
|
1104
|
+
|
|
871
1105
|
```ts
|
|
872
|
-
expect(
|
|
873
|
-
|
|
1106
|
+
expect(nanolightTs('/^(?:\\d{1,3}|[^a-z\\]])+?\\b(?<year>\\d{4})-\\k<year>\\1$/u')).toEqual([
|
|
1107
|
+
['span', { class: 'regexp' }, '/'],
|
|
1108
|
+
['span', { class: 'keyword1' }, '^'],
|
|
1109
|
+
['span', { class: 'punctuation' }, '(?:'],
|
|
1110
|
+
['span', { class: 'keyword3' }, '\\d'],
|
|
1111
|
+
['span', { class: 'operator' }, '{1,3}'],
|
|
1112
|
+
['span', { class: 'operator' }, '|'],
|
|
1113
|
+
['span', { class: 'punctuation' }, '['],
|
|
1114
|
+
['span', { class: 'operator' }, '^'],
|
|
1115
|
+
['span', { class: 'regexp' }, 'a'],
|
|
1116
|
+
['span', { class: 'operator' }, '-'],
|
|
1117
|
+
['span', { class: 'regexp' }, 'z'],
|
|
1118
|
+
['span', { class: 'keyword2' }, '\\]'],
|
|
1119
|
+
['span', { class: 'punctuation' }, ']'],
|
|
1120
|
+
['span', { class: 'punctuation' }, ')'],
|
|
1121
|
+
['span', { class: 'operator' }, '+?'],
|
|
1122
|
+
['span', { class: 'keyword1' }, '\\b'],
|
|
1123
|
+
['span', { class: 'punctuation' }, '(?<'],
|
|
1124
|
+
['span', { class: 'identifier2' }, 'year'],
|
|
1125
|
+
['span', { class: 'punctuation' }, '>'],
|
|
1126
|
+
['span', { class: 'keyword3' }, '\\d'],
|
|
1127
|
+
['span', { class: 'operator' }, '{4}'],
|
|
1128
|
+
['span', { class: 'punctuation' }, ')'],
|
|
1129
|
+
['span', { class: 'regexp' }, '-'],
|
|
1130
|
+
['span', { class: 'identifier2' }, '\\k<year>'],
|
|
1131
|
+
['span', { class: 'identifier2' }, '\\1'],
|
|
1132
|
+
['span', { class: 'keyword1' }, '$'],
|
|
1133
|
+
['span', { class: 'regexp' }, '/'],
|
|
1134
|
+
['span', { class: 'keyword1' }, 'u'],
|
|
1135
|
+
])
|
|
1136
|
+
|
|
1137
|
+
expect(nanolightTs('/(?<=\\$)(?!0)\\p{L}+\\.\\u{1F600}|\\x41[.\\b-]/v')).toEqual([
|
|
1138
|
+
['span', { class: 'regexp' }, '/'],
|
|
1139
|
+
['span', { class: 'punctuation' }, '(?<='],
|
|
1140
|
+
['span', { class: 'keyword2' }, '\\$'],
|
|
1141
|
+
['span', { class: 'punctuation' }, ')'],
|
|
1142
|
+
['span', { class: 'punctuation' }, '(?!'],
|
|
1143
|
+
['span', { class: 'regexp' }, '0'],
|
|
1144
|
+
['span', { class: 'punctuation' }, ')'],
|
|
1145
|
+
['span', { class: 'keyword3' }, '\\p{L}'],
|
|
1146
|
+
['span', { class: 'operator' }, '+'],
|
|
1147
|
+
['span', { class: 'keyword2' }, '\\.'],
|
|
1148
|
+
['span', { class: 'keyword2' }, '\\u{1F600}'],
|
|
1149
|
+
['span', { class: 'operator' }, '|'],
|
|
1150
|
+
['span', { class: 'keyword2' }, '\\x41'],
|
|
1151
|
+
['span', { class: 'punctuation' }, '['],
|
|
1152
|
+
['span', { class: 'regexp' }, '.'],
|
|
1153
|
+
['span', { class: 'keyword2' }, '\\b'],
|
|
1154
|
+
['span', { class: 'regexp' }, '-'],
|
|
1155
|
+
['span', { class: 'punctuation' }, ']'],
|
|
1156
|
+
['span', { class: 'regexp' }, '/'],
|
|
1157
|
+
['span', { class: 'keyword1' }, 'v'],
|
|
1158
|
+
])
|
|
874
1159
|
```
|
|
875
1160
|
|
|
876
1161
|
### rwd
|
|
@@ -885,7 +1170,8 @@ const rwd: (
|
|
|
885
1170
|
) => void
|
|
886
1171
|
```
|
|
887
1172
|
|
|
888
|
-
A responsive web design helper that generates CSS rules for a grid-like layout.
|
|
1173
|
+
A responsive web design helper that generates CSS rules for a grid-like layout of floating elements.
|
|
1174
|
+
It modifies `root` in place: it sets base styles for `selector` and adds a `@media(min-width:…px)` rule for each breakpoint.
|
|
889
1175
|
|
|
890
1176
|
#### root
|
|
891
1177
|
|
|
@@ -907,12 +1193,14 @@ The base cell height in pixels.
|
|
|
907
1193
|
|
|
908
1194
|
An array of breakpoint specifications, each a tuple of:
|
|
909
1195
|
|
|
910
|
-
- `maxWidth`:
|
|
1196
|
+
- `maxWidth`: number of cells per row; the breakpoint is `min-width: cellWidthPx * maxWidth` (`1` modifies the base rule).
|
|
911
1197
|
- `width` (optional, default `1`): number of horizontal cells the element spans.
|
|
912
1198
|
- `height` (optional, default `1`): number of vertical cells the element spans.
|
|
913
1199
|
|
|
914
1200
|
#### Usage Examples
|
|
915
1201
|
|
|
1202
|
+
Responsive grid cell widths and heights with media queries:
|
|
1203
|
+
|
|
916
1204
|
```ts
|
|
917
1205
|
const style: CNode = {
|
|
918
1206
|
body: {
|
|
@@ -937,7 +1225,7 @@ expect(style).toEqual({
|
|
|
937
1225
|
'.no-border': {
|
|
938
1226
|
border: 'none',
|
|
939
1227
|
},
|
|
940
|
-
|
|
1228
|
+
'box-sizing': 'border-box',
|
|
941
1229
|
display: 'block',
|
|
942
1230
|
float: 'left',
|
|
943
1231
|
width: '100%',
|
|
@@ -962,9 +1250,9 @@ expect(style).toEqual({
|
|
|
962
1250
|
|
|
963
1251
|
```ts
|
|
964
1252
|
const s: {
|
|
965
|
-
<T extends keyof SVGElementTagNameMap>(tag: T, ...
|
|
966
|
-
<N extends Node>(node: N, ...
|
|
967
|
-
(tagOrNode: string | Node, ...
|
|
1253
|
+
<T extends keyof SVGElementTagNameMap>(tag: T, ...args: HArgs1[]): SVGElementTagNameMap[T]
|
|
1254
|
+
<N extends Node>(node: N, ...args: HArgs1[]): N
|
|
1255
|
+
(tagOrNode: string | Node, ...args: HArgs1[]): Node
|
|
968
1256
|
}
|
|
969
1257
|
```
|
|
970
1258
|
|
|
@@ -980,38 +1268,46 @@ If a `Node`, that node is modified in place.
|
|
|
980
1268
|
Additional arguments processed as follows:
|
|
981
1269
|
|
|
982
1270
|
- `Object`: maps attributes/properties.
|
|
983
|
-
Keys starting with `$` set element properties (without the `$` prefix); if the value is a plain object, it is shallow-merged into the existing property instead of replacing it.
|
|
984
|
-
Other keys set attributes: a key prefixed with `xlink:` sets the attribute via `setAttributeNS` in the XLink namespace; any other key (without a colon) sets it via `setAttribute`.
|
|
985
|
-
|
|
1271
|
+
- Keys starting with `$` set element properties (without the `$` prefix); if the value is a plain object, it is shallow-merged into the existing property instead of replacing it.
|
|
1272
|
+
- Other keys set attributes: a key prefixed with `xlink:` sets the attribute via `setAttributeNS` in the XLink namespace; any other key (without a colon) sets it via `setAttribute`.
|
|
1273
|
+
A value of `true` sets an empty attribute (e.g. `checked=""`), a value of `false` removes the attribute.
|
|
986
1274
|
- `null`/`undefined`: ignored.
|
|
987
1275
|
- `Node`: appended as a child.
|
|
988
1276
|
- `string`/`number`: converted to a `Text` node and appended.
|
|
989
|
-
- [`HArgs`](#HArgs) array: processed recursively.
|
|
1277
|
+
- [`HArgs`](#HArgs) array: processed recursively (creates or modifies a child node and appends it).
|
|
990
1278
|
|
|
991
1279
|
#### Returns
|
|
992
1280
|
|
|
993
|
-
The created or modified
|
|
1281
|
+
The created `SVGElement` or the modified node.
|
|
994
1282
|
|
|
995
1283
|
#### Usage Examples
|
|
996
1284
|
|
|
1285
|
+
Namespaced `xlink:` attribute set to `true` gets an empty value:
|
|
1286
|
+
|
|
997
1287
|
```ts
|
|
998
1288
|
const svg = s('svg', { 'xlink:href': true })
|
|
999
1289
|
|
|
1000
1290
|
expect(svg.getAttributeNS('http://www.w3.org/1999/xlink', 'href')).toBe('')
|
|
1001
1291
|
```
|
|
1002
1292
|
|
|
1293
|
+
Namespaced `xlink:` attribute set to `false` is not set:
|
|
1294
|
+
|
|
1003
1295
|
```ts
|
|
1004
1296
|
const svg = s('svg', { 'xlink:href': false })
|
|
1005
1297
|
|
|
1006
1298
|
expect(svg.getAttributeNS('http://www.w3.org/1999/xlink', 'href')).toBeNull()
|
|
1007
1299
|
```
|
|
1008
1300
|
|
|
1301
|
+
Namespaced `xlink:` attribute with a string value:
|
|
1302
|
+
|
|
1009
1303
|
```ts
|
|
1010
1304
|
const svg = s('svg', { 'xlink:href': 'http://example.com' })
|
|
1011
1305
|
|
|
1012
1306
|
expect(svg.getAttributeNS('http://www.w3.org/1999/xlink', 'href')).toBe('http://example.com')
|
|
1013
1307
|
```
|
|
1014
1308
|
|
|
1309
|
+
Namespaced `xlink:` attribute with a number value:
|
|
1310
|
+
|
|
1015
1311
|
```ts
|
|
1016
1312
|
const svg = s('svg', { 'xlink:title': 42 })
|
|
1017
1313
|
|
|
@@ -1024,7 +1320,7 @@ expect(svg.getAttributeNS('http://www.w3.org/1999/xlink', 'title')).toBe('42')
|
|
|
1024
1320
|
const svgUse: (id: string, ...args: HArgs1[]) => SVGSVGElement
|
|
1025
1321
|
```
|
|
1026
1322
|
|
|
1027
|
-
Shorthand for creating an
|
|
1323
|
+
Shorthand for creating an `<svg>` element with a `<use>` child referencing a symbol (e.g. an icon) by ID.
|
|
1028
1324
|
|
|
1029
1325
|
Equivalent to:
|
|
1030
1326
|
|
|
@@ -1046,6 +1342,8 @@ An `SVGSVGElement` containing a `<use>` element.
|
|
|
1046
1342
|
|
|
1047
1343
|
#### Usage Examples
|
|
1048
1344
|
|
|
1345
|
+
SVG `use` element referencing a symbol, with extra attributes:
|
|
1346
|
+
|
|
1049
1347
|
```ts
|
|
1050
1348
|
const svg = svgUse('icon-home')
|
|
1051
1349
|
|
package/nnn.d.ts
CHANGED
|
@@ -9,12 +9,15 @@ export type CNode = {
|
|
|
9
9
|
*
|
|
10
10
|
* An object describing CSS rules.
|
|
11
11
|
* Keys are selectors or at-rules; values are either CSS property values or nested rule objects.
|
|
12
|
+
* Nested selectors are appended to the parent selector as is (e.g. `'.c'` inside `a` → `a.c`), so a leading space is needed for a descendant combinator (e.g. `' .c'` → `a .c`).
|
|
12
13
|
* A selector key may contain comma-separated alternatives (e.g. `'.b,.c'`); when nested, each alternative is combined with each parent alternative (cartesian product), producing a comma-joined selector list (e.g. `'a.b,a.c'`).
|
|
14
|
+
* Top-level at-rules (e.g. `'@media …'`) wrap their nested rules; inside a top-level `:root` rule nested keys are concatenated into property names (e.g. `{ ':root': { '--': { bg: 'red' } } }` → `:root{--bg:red}`).
|
|
15
|
+
* `null`/`undefined` values are skipped.
|
|
13
16
|
*
|
|
14
17
|
* @param splitter
|
|
15
18
|
*
|
|
16
19
|
* A delimiter used to create unique keys (default: `'$$'`).
|
|
17
|
-
* The substring from `splitter` to the end of a key is ignored (e.g
|
|
20
|
+
* The substring from `splitter` to the end of a key is ignored (e.g. `src$$1` → `src`).
|
|
18
21
|
*
|
|
19
22
|
* @returns
|
|
20
23
|
*
|
|
@@ -22,7 +25,7 @@ export type CNode = {
|
|
|
22
25
|
*/
|
|
23
26
|
export declare const c: (node: CNode, splitter?: string) => string
|
|
24
27
|
/**
|
|
25
|
-
* Options of a {@link
|
|
28
|
+
* Options of a {@link createLexer} spec controlling the lexer state stack.
|
|
26
29
|
*
|
|
27
30
|
* @property in
|
|
28
31
|
*
|
|
@@ -36,15 +39,15 @@ export declare const c: (node: CNode, splitter?: string) => string
|
|
|
36
39
|
*
|
|
37
40
|
* State to push onto the stack after a match (applied after `pop`).
|
|
38
41
|
*/
|
|
39
|
-
export type
|
|
42
|
+
export type LexerSpecOptions = {
|
|
40
43
|
in?: string | string[]
|
|
41
44
|
pop?: boolean
|
|
42
45
|
push?: string
|
|
43
46
|
}
|
|
44
|
-
/** A {@link
|
|
45
|
-
export type
|
|
47
|
+
/** A {@link createLexer} spec: a `[metadata, pattern, options?]` tuple. */
|
|
48
|
+
export type LexerSpec<M> = [metadata: M, pattern: string | RegExp, options?: LexerSpecOptions]
|
|
46
49
|
/**
|
|
47
|
-
* A helper for building simple stateful
|
|
50
|
+
* A helper for building simple stateful lexers (see also {@link nanolightTs}).
|
|
48
51
|
*
|
|
49
52
|
* @remarks
|
|
50
53
|
*
|
|
@@ -57,25 +60,25 @@ export type TokenizerSpec<M> = [metadata: M, pattern: string | RegExp, options?:
|
|
|
57
60
|
*
|
|
58
61
|
* @param decorator
|
|
59
62
|
*
|
|
60
|
-
* A function that wraps each
|
|
61
|
-
* It receives the matched text (`chunk`) and
|
|
62
|
-
* For unmatched text between
|
|
63
|
+
* A function that wraps each chunk of the input.
|
|
64
|
+
* It receives the matched text (`chunk`) and the `metadata` of the spec whose pattern produced the match.
|
|
65
|
+
* For unmatched text between matches, `metadata` is `undefined`.
|
|
63
66
|
*
|
|
64
67
|
* @param specs
|
|
65
68
|
*
|
|
66
69
|
* An array of tuples `[metadata, pattern, options?]` where:
|
|
67
70
|
*
|
|
68
71
|
* - `metadata`: arbitrary data (e.g., a CSS class name) passed to `decorator` when the pattern matches.
|
|
69
|
-
* - `pattern`: a `string` or `RegExp` to match against the input.
|
|
70
|
-
* - `options`: optional {@link
|
|
72
|
+
* - `pattern`: a `string` (matched literally) or `RegExp` to match against the input.
|
|
73
|
+
* - `options`: optional {@link LexerSpecOptions} (states in which the spec is active and state transitions).
|
|
71
74
|
*
|
|
72
75
|
* @returns
|
|
73
76
|
*
|
|
74
|
-
* A
|
|
77
|
+
* A lexer function that accepts an input string and returns an array of decorated chunks covering the whole input (in order).
|
|
75
78
|
*/
|
|
76
|
-
export declare const
|
|
79
|
+
export declare const createLexer: <M, T>(
|
|
77
80
|
decorator: (chunk: string, metadata?: M) => T,
|
|
78
|
-
...specs:
|
|
81
|
+
...specs: LexerSpec<M>[]
|
|
79
82
|
) => (code: string) => T[]
|
|
80
83
|
/** Single argument type for the {@link h} and {@link s} helpers. */
|
|
81
84
|
export type HArgs1 = Record<PropertyKey, unknown> | null | undefined | Node | string | number | HArgs
|
|
@@ -94,22 +97,22 @@ export type HArgs = [string | Node, ...HArgs1[]]
|
|
|
94
97
|
* Additional arguments processed as follows:
|
|
95
98
|
*
|
|
96
99
|
* - `Object`: maps attributes/properties.
|
|
97
|
-
* Keys starting with `$` set element properties (without the `$` prefix); if the value is a plain object, it is shallow-merged into the existing property instead of replacing it (useful e.g. for `$style`).
|
|
98
|
-
* Other keys set attributes: a key prefixed with `xlink:` sets the attribute via `setAttributeNS` in the XLink namespace; any other key (without a colon) sets it via `setAttribute`.
|
|
99
|
-
*
|
|
100
|
+
* - Keys starting with `$` set element properties (without the `$` prefix); if the value is a plain object, it is shallow-merged into the existing property instead of replacing it (useful e.g. for `$style`).
|
|
101
|
+
* - Other keys set attributes: a key prefixed with `xlink:` sets the attribute via `setAttributeNS` in the XLink namespace; any other key (without a colon) sets it via `setAttribute`.
|
|
102
|
+
* A value of `true` sets an empty attribute (e.g. `checked=""`), a value of `false` removes the attribute.
|
|
100
103
|
* - `null`/`undefined`: ignored.
|
|
101
104
|
* - `Node`: appended as a child.
|
|
102
105
|
* - `string`/`number`: converted to a `Text` node and appended.
|
|
103
|
-
* - {@link HArgs} array: processed recursively.
|
|
106
|
+
* - {@link HArgs} array: processed recursively (creates or modifies a child node and appends it).
|
|
104
107
|
*
|
|
105
108
|
* @returns
|
|
106
109
|
*
|
|
107
|
-
* The created or modified
|
|
110
|
+
* The created `HTMLElement` or the modified node.
|
|
108
111
|
*/
|
|
109
112
|
export declare const h: {
|
|
110
|
-
<T extends keyof HTMLElementTagNameMap>(tag: T, ...
|
|
111
|
-
<N extends Node>(node: N, ...
|
|
112
|
-
(tagOrNode: string | Node, ...
|
|
113
|
+
<T extends keyof HTMLElementTagNameMap>(tag: T, ...args: HArgs1[]): HTMLElementTagNameMap[T]
|
|
114
|
+
<N extends Node>(node: N, ...args: HArgs1[]): N
|
|
115
|
+
(tagOrNode: string | Node, ...args: HArgs1[]): Node
|
|
113
116
|
}
|
|
114
117
|
/**
|
|
115
118
|
* A lightweight _HyperScript_-style helper for creating and modifying `SVGElement`s (see also {@link h}).
|
|
@@ -124,25 +127,25 @@ export declare const h: {
|
|
|
124
127
|
* Additional arguments processed as follows:
|
|
125
128
|
*
|
|
126
129
|
* - `Object`: maps attributes/properties.
|
|
127
|
-
* Keys starting with `$` set element properties (without the `$` prefix); if the value is a plain object, it is shallow-merged into the existing property instead of replacing it.
|
|
128
|
-
* Other keys set attributes: a key prefixed with `xlink:` sets the attribute via `setAttributeNS` in the XLink namespace; any other key (without a colon) sets it via `setAttribute`.
|
|
129
|
-
*
|
|
130
|
+
* - Keys starting with `$` set element properties (without the `$` prefix); if the value is a plain object, it is shallow-merged into the existing property instead of replacing it.
|
|
131
|
+
* - Other keys set attributes: a key prefixed with `xlink:` sets the attribute via `setAttributeNS` in the XLink namespace; any other key (without a colon) sets it via `setAttribute`.
|
|
132
|
+
* A value of `true` sets an empty attribute (e.g. `checked=""`), a value of `false` removes the attribute.
|
|
130
133
|
* - `null`/`undefined`: ignored.
|
|
131
134
|
* - `Node`: appended as a child.
|
|
132
135
|
* - `string`/`number`: converted to a `Text` node and appended.
|
|
133
|
-
* - {@link HArgs} array: processed recursively.
|
|
136
|
+
* - {@link HArgs} array: processed recursively (creates or modifies a child node and appends it).
|
|
134
137
|
*
|
|
135
138
|
* @returns
|
|
136
139
|
*
|
|
137
|
-
* The created or modified
|
|
140
|
+
* The created `SVGElement` or the modified node.
|
|
138
141
|
*/
|
|
139
142
|
export declare const s: {
|
|
140
|
-
<T extends keyof SVGElementTagNameMap>(tag: T, ...
|
|
141
|
-
<N extends Node>(node: N, ...
|
|
142
|
-
(tagOrNode: string | Node, ...
|
|
143
|
+
<T extends keyof SVGElementTagNameMap>(tag: T, ...args: HArgs1[]): SVGElementTagNameMap[T]
|
|
144
|
+
<N extends Node>(node: N, ...args: HArgs1[]): N
|
|
145
|
+
(tagOrNode: string | Node, ...args: HArgs1[]): Node
|
|
143
146
|
}
|
|
144
147
|
/**
|
|
145
|
-
* Shorthand for creating an
|
|
148
|
+
* Shorthand for creating an `<svg>` element with a `<use>` child referencing a symbol (e.g. an icon) by ID.
|
|
146
149
|
*
|
|
147
150
|
* Equivalent to:
|
|
148
151
|
*
|
|
@@ -164,11 +167,12 @@ export declare const s: {
|
|
|
164
167
|
*/
|
|
165
168
|
export declare const svgUse: (id: string, ...args: HArgs1[]) => SVGSVGElement
|
|
166
169
|
/**
|
|
167
|
-
* A Monokai-inspired color scheme for use with the {@link c} helper and {@link nanolightTs}
|
|
170
|
+
* A Monokai-inspired color scheme for use with the {@link c} helper and {@link nanolightTs} lexer.
|
|
171
|
+
* It styles `pre` and `code>span` elements with the class names produced by {@link nanolightTs} (with light, dark and print variants).
|
|
168
172
|
*/
|
|
169
173
|
export declare const monokai: CNode
|
|
170
174
|
/**
|
|
171
|
-
* A TypeScript/JavaScript syntax highlighting
|
|
175
|
+
* A TypeScript/JavaScript syntax highlighting lexer built using {@link createLexer}.
|
|
172
176
|
*
|
|
173
177
|
* @param code
|
|
174
178
|
*
|
|
@@ -176,11 +180,12 @@ export declare const monokai: CNode
|
|
|
176
180
|
*
|
|
177
181
|
* @returns
|
|
178
182
|
*
|
|
179
|
-
* An array of {@link HArgs1} elements suitable for rendering with {@link h}.
|
|
183
|
+
* An array of {@link HArgs1} elements suitable for rendering with {@link h}: plain `string`s for unrecognized text (e.g. whitespace) and `['span', { class }, chunk]` tuples for recognized tokens (class names are styled by {@link monokai}).
|
|
180
184
|
*/
|
|
181
185
|
export declare const nanolightTs: (code: string) => HArgs1[]
|
|
182
186
|
/**
|
|
183
|
-
* A responsive web design helper that generates CSS rules for a grid-like layout.
|
|
187
|
+
* A responsive web design helper that generates CSS rules for a grid-like layout of floating elements.
|
|
188
|
+
* It modifies `root` in place: it sets base styles for `selector` and adds a `@media(min-width:…px)` rule for each breakpoint.
|
|
184
189
|
*
|
|
185
190
|
* @param root
|
|
186
191
|
*
|
|
@@ -202,7 +207,7 @@ export declare const nanolightTs: (code: string) => HArgs1[]
|
|
|
202
207
|
*
|
|
203
208
|
* An array of breakpoint specifications, each a tuple of:
|
|
204
209
|
*
|
|
205
|
-
* - `maxWidth`:
|
|
210
|
+
* - `maxWidth`: number of cells per row; the breakpoint is `min-width: cellWidthPx * maxWidth` (`1` modifies the base rule).
|
|
206
211
|
* - `width` (optional, default `1`): number of horizontal cells the element spans.
|
|
207
212
|
* - `height` (optional, default `1`): number of vertical cells the element spans.
|
|
208
213
|
*/
|
package/nnn.js
CHANGED
|
@@ -50,9 +50,9 @@ var _c = (node, splitter = DEFAULT_SPLITTER, prefix = "", flatten = false) => {
|
|
|
50
50
|
return out.join(flatten ? ";" : "");
|
|
51
51
|
};
|
|
52
52
|
var c = (node, splitter = DEFAULT_SPLITTER) => _c(node, splitter);
|
|
53
|
-
// src/nnn/
|
|
53
|
+
// src/nnn/createLexer.ts
|
|
54
54
|
var ROOT = "root";
|
|
55
|
-
var
|
|
55
|
+
var createLexer = (decorator, ...specs) => {
|
|
56
56
|
const compiled = specs.map(([metadata, pattern, options]) => ({
|
|
57
57
|
metadata,
|
|
58
58
|
pattern: pattern instanceof RegExp ? new RegExp(pattern.source, pattern.sticky ? pattern.flags : pattern.flags.replace("g", "") + "g") : pattern,
|
|
@@ -201,6 +201,7 @@ var monokai = {
|
|
|
201
201
|
number: "#7058be",
|
|
202
202
|
operator: "#e14775",
|
|
203
203
|
punctuation: "#918c8e",
|
|
204
|
+
regexp: "#e16032",
|
|
204
205
|
string: "#cc7a0a"
|
|
205
206
|
}
|
|
206
207
|
},
|
|
@@ -233,6 +234,7 @@ var monokai = {
|
|
|
233
234
|
number: { color: "var(--number)" },
|
|
234
235
|
operator: { color: "var(--operator)" },
|
|
235
236
|
punctuation: { color: "var(--punctuation)" },
|
|
237
|
+
regexp: { color: "var(--regexp)" },
|
|
236
238
|
string: { color: "var(--string)" }
|
|
237
239
|
},
|
|
238
240
|
"@media only screen and (prefers-color-scheme: dark)$$monokai": {
|
|
@@ -255,6 +257,7 @@ var monokai = {
|
|
|
255
257
|
number: "#ae81ff",
|
|
256
258
|
operator: "#ff6188",
|
|
257
259
|
punctuation: "#727072",
|
|
260
|
+
regexp: "#fc9867",
|
|
258
261
|
string: "#ffd866"
|
|
259
262
|
}
|
|
260
263
|
}
|
|
@@ -280,17 +283,24 @@ var KEYWORD3 = "keyword3";
|
|
|
280
283
|
var NUMBER = "number";
|
|
281
284
|
var OPERATOR = "operator";
|
|
282
285
|
var PUNCTUATION = "punctuation";
|
|
286
|
+
var REGEXP = "regexp";
|
|
283
287
|
var STRING = "string";
|
|
284
288
|
var ROOT2 = "root";
|
|
285
289
|
var TEMPLATE = "template";
|
|
286
|
-
var
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
+
var REGEXP_BODY = "regexpBody";
|
|
291
|
+
var REGEXP_FLAGS = "regexpFlags";
|
|
292
|
+
var REGEXP_GROUP_NAME = "regexpGroupName";
|
|
293
|
+
var REGEXP_SET = "regexpSet";
|
|
294
|
+
var REGEXP_SET_NEGATION = "regexpSetNegation";
|
|
295
|
+
var nanolightTs = /* @__PURE__ */ createLexer((chunk, name) => name != null ? ["span", { class: name }, chunk] : chunk, [COMMENT, /^#!.*/], [COMMENT, /\/\*[\s\S]*?(?:\*\/|$)/], [COMMENT, /\/\/.*/], [STRING, /"(?:[^"\\\n]|\\[\s\S])*"?/], [STRING, /'(?:[^'\\\n]|\\[\s\S])*'?/], [STRING, /`(?:[^`\\$]|\\[\s\S]|\$(?!\{))*(?:`|$)/], [STRING, /`(?:[^`\\$]|\\[\s\S]|\$(?!\{))*(?=\$\{)/, { push: TEMPLATE }], [STRING, /(?:[^`\\$]|\\[\s\S]|\$(?!\{))*(?:`|$)/y, { in: TEMPLATE, pop: true }], [STRING, /(?:[^`\\$]|\\[\s\S]|\$(?!\{))+(?=\$\{)/y, { in: TEMPLATE }], [PUNCTUATION, "${", { in: TEMPLATE, push: ROOT2 }], [
|
|
296
|
+
REGEXP,
|
|
297
|
+
/(?<=(?:^|[!%&(*,:;<=>?[^{|}~]|(?<!\+)\+|(?<!-)-|(?<!\.)\b(?:await|case|delete|do|else|in|instanceof|new|of|return|throw|typeof|void|yield))\s*)\/(?![*/])(?=(?:[^\\/[\n]|\\.|\[(?:[^\\\]\n]|\\.)*\])+\/)=?/,
|
|
298
|
+
{ push: REGEXP_BODY }
|
|
299
|
+
], [REGEXP, /\/(?=[dgimsuvy])/y, { in: REGEXP_BODY, pop: true, push: REGEXP_FLAGS }], [REGEXP, /\/(?![dgimsuvy])/y, { in: REGEXP_BODY, pop: true }], [KEYWORD1, /[dgimsuvy]+/y, { in: REGEXP_FLAGS, pop: true }], [IDENTIFIER2, /\\[1-9]\d*|\\k<[\p{L}_$][\p{L}\d_$]*>/uy, { in: REGEXP_BODY }], [KEYWORD1, /\\[bB]/y, { in: REGEXP_BODY }], [KEYWORD3, /\\[dDsSwW]|\\[pP]\{[\w=]*\}/y, { in: [REGEXP_BODY, REGEXP_SET] }], [KEYWORD2, /\\(?:c[A-Za-z]|x[\da-fA-F]{2}|u[\da-fA-F]{4}|u\{[\da-fA-F]+\}|.)/uy, { in: [REGEXP_BODY, REGEXP_SET] }], [KEYWORD1, /[$^]/y, { in: REGEXP_BODY }], [KEYWORD3, ".", { in: REGEXP_BODY }], [OPERATOR, /(?:[*+?]|\{\d+(?:,\d*)?\})\??/y, { in: REGEXP_BODY }], [OPERATOR, "|", { in: REGEXP_BODY }], [PUNCTUATION, /\((?:\?(?:[:=!]|<[=!]))?/y, { in: REGEXP_BODY }], [PUNCTUATION, /\(\?<(?=[\p{L}_$][\p{L}\d_$]*>)/uy, { in: REGEXP_BODY, push: REGEXP_GROUP_NAME }], [IDENTIFIER2, /[\p{L}_$][\p{L}\d_$]*/uy, { in: REGEXP_GROUP_NAME }], [PUNCTUATION, ">", { in: REGEXP_GROUP_NAME, pop: true }], [PUNCTUATION, ")", { in: REGEXP_BODY }], [PUNCTUATION, /\[(?!\^)/y, { in: REGEXP_BODY, push: REGEXP_SET }], [PUNCTUATION, /\[(?=\^)/y, { in: REGEXP_BODY, push: REGEXP_SET_NEGATION }], [OPERATOR, "^", { in: REGEXP_SET_NEGATION, pop: true, push: REGEXP_SET }], [OPERATOR, /(?<!(?<!\\)\[\^?)-(?!\])/y, { in: REGEXP_SET }], [PUNCTUATION, "]", { in: REGEXP_SET, pop: true }], [REGEXP, /[^\\/[()|?*+{^$.\n]+/uy, { in: REGEXP_BODY }], [REGEXP, /[^\\\]\-\n]+/uy, { in: REGEXP_SET }], [REGEXP, /[^\n]/uy, { in: [REGEXP_BODY, REGEXP_SET] }], [NUMBER, /0b[01_]+n?/i], [NUMBER, /0o[0-7_]+n?/i], [NUMBER, /0x[\da-f_]+n?/i], [NUMBER, /(?:\d[\d_]*(?:\.[\d_]+)?|\.\d[\d_]*)(?:e[+-]?\d[\d_]*)?n?/i], [OPERATOR, "!"], [OPERATOR, "!="], [OPERATOR, "!=="], [OPERATOR, "%"], [OPERATOR, "%="], [OPERATOR, "&&"], [OPERATOR, "&&="], [OPERATOR, "&"], [OPERATOR, "&="], [OPERATOR, "*"], [OPERATOR, "**"], [OPERATOR, "**="], [OPERATOR, "*="], [OPERATOR, "+"], [OPERATOR, "++"], [OPERATOR, "+="], [OPERATOR, "-"], [OPERATOR, "--"], [OPERATOR, "-="], [OPERATOR, "..."], [OPERATOR, "/"], [OPERATOR, "/="], [OPERATOR, ":"], [OPERATOR, "<"], [OPERATOR, "<<"], [OPERATOR, "<<="], [OPERATOR, "<="], [OPERATOR, "="], [OPERATOR, "=="], [OPERATOR, "==="], [OPERATOR, "=>"], [OPERATOR, ">"], [OPERATOR, ">="], [OPERATOR, ">>"], [OPERATOR, ">>="], [OPERATOR, ">>>"], [OPERATOR, ">>>="], [OPERATOR, "?"], [OPERATOR, "??"], [OPERATOR, "??="], [OPERATOR, "^"], [OPERATOR, "^="], [OPERATOR, "|"], [OPERATOR, "|="], [OPERATOR, "||"], [OPERATOR, "||="], [OPERATOR, "~"], [OPERATOR, "~="], [OPERATOR, /(?<=\s):/], [PUNCTUATION, "("], [PUNCTUATION, ")"], [PUNCTUATION, ","], [PUNCTUATION, "."], [PUNCTUATION, ":"], [PUNCTUATION, ";"], [PUNCTUATION, /\?\.(?!\d)/], [PUNCTUATION, "["], [PUNCTUATION, "]"], [PUNCTUATION, "{", { push: ROOT2 }], [PUNCTUATION, "}", { pop: true }], [IDENTIFIER2, /(?<=(?<!\.)\.)\p{Ll}[\p{L}\d_$]*(?=[(`])/u], [IDENTIFIER4, /(?<=(?<!\.)\.)\p{Ll}[\p{L}\d_$]*/u], [KEYWORD1, "as"], [KEYWORD1, "async"], [KEYWORD1, "await"], [KEYWORD1, "break"], [KEYWORD1, "case"], [KEYWORD1, "catch"], [KEYWORD1, "class"], [KEYWORD1, "const"], [KEYWORD1, "continue"], [KEYWORD1, "debugger"], [KEYWORD1, "default"], [KEYWORD1, "delete"], [KEYWORD1, "do"], [KEYWORD1, "else"], [KEYWORD1, "export"], [KEYWORD1, "extends"], [KEYWORD1, "finally"], [KEYWORD1, "for"], [KEYWORD1, "from"], [KEYWORD1, "function"], [KEYWORD1, "function*"], [KEYWORD1, "goto"], [KEYWORD1, "if"], [KEYWORD1, "import"], [KEYWORD1, "in"], [KEYWORD1, "instanceof"], [KEYWORD1, "is"], [KEYWORD1, "keyof"], [KEYWORD1, "let"], [KEYWORD1, "new"], [KEYWORD1, "of"], [KEYWORD1, "package"], [KEYWORD1, "return"], [KEYWORD1, "super"], [KEYWORD1, "switch"], [KEYWORD1, "this"], [KEYWORD1, "throw"], [KEYWORD1, "try"], [KEYWORD1, "type"], [KEYWORD1, "typeof"], [KEYWORD1, "var"], [KEYWORD1, "void"], [KEYWORD1, "while"], [KEYWORD1, "with"], [KEYWORD1, "yield"], [KEYWORD1, "yield*"], [KEYWORD2, "false"], [KEYWORD2, "Infinity"], [KEYWORD2, "NaN"], [KEYWORD2, "null"], [KEYWORD2, "true"], [KEYWORD2, "undefined"], [KEYWORD3, "any"], [KEYWORD3, "bigint"], [KEYWORD3, "boolean"], [KEYWORD3, "eval"], [KEYWORD3, "number"], [KEYWORD3, "string"], [KEYWORD3, "symbol"], [KEYWORD3, "unknown"], [IDENTIFIER2, /@[\p{L}_$][\p{L}\d_$]*/u], [IDENTIFIER1, /#?[\p{Lu}_$][\p{Lu}\d_$]*/u], [IDENTIFIER2, /#?[\p{L}_$][\p{L}\d_$]*(?=[(`])/u], [IDENTIFIER3, /#?\p{Lu}[\p{L}\d_$]*/u], [IDENTIFIER4, /#?[\p{L}_$][\p{L}\d_$]*/u]);
|
|
290
300
|
// src/nnn/rwd.ts
|
|
291
301
|
var rwd = (root, selector, cellWidthPx, cellHeightPx, ...specs) => {
|
|
292
302
|
const main = root[selector] ??= {};
|
|
293
|
-
main
|
|
303
|
+
main["box-sizing"] = "border-box";
|
|
294
304
|
main.display = "block";
|
|
295
305
|
main.float = "left";
|
|
296
306
|
main.width = "100%";
|
|
@@ -313,7 +323,7 @@ var rwd = (root, selector, cellWidthPx, cellHeightPx, ...specs) => {
|
|
|
313
323
|
};
|
|
314
324
|
export {
|
|
315
325
|
c,
|
|
316
|
-
|
|
326
|
+
createLexer,
|
|
317
327
|
h,
|
|
318
328
|
monokai,
|
|
319
329
|
nanolightTs,
|
package/package.json
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jackens/nnn",
|
|
3
|
-
"version": "2026.9.
|
|
3
|
+
"version": "2026.9.30",
|
|
4
4
|
"description": "Jackens’ JavaScript helpers.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"CSS-in-JS",
|
|
7
|
+
"highlight",
|
|
7
8
|
"HyperScript",
|
|
8
9
|
"JS-to-CSS",
|
|
10
|
+
"lexer",
|
|
9
11
|
"Monokai",
|
|
10
|
-
"RWD",
|
|
11
|
-
"Responsive Web Design",
|
|
12
|
-
"SVG",
|
|
13
|
-
"highlight",
|
|
14
12
|
"nanolight",
|
|
15
13
|
"nnn",
|
|
14
|
+
"Responsive Web Design",
|
|
15
|
+
"RWD",
|
|
16
|
+
"SVG",
|
|
16
17
|
"tokenize",
|
|
17
18
|
"tokenizer"
|
|
18
19
|
],
|