@jackens/nnn 2026.9.29 → 2026.10.1
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 +41 -476
- package/nnn.d.ts +0 -73
- package/nnn.js +0 -183
- package/package.json +3 -9
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# nnn
|
|
2
2
|
|
|
3
|
-
A collection of Jackens’ JavaScript helper utilities (version: `2026.
|
|
3
|
+
A collection of Jackens’ JavaScript helper utilities (version: `2026.10.1`).
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -21,13 +21,8 @@ import {
|
|
|
21
21
|
CNode,
|
|
22
22
|
HArgs,
|
|
23
23
|
HArgs1,
|
|
24
|
-
LexerSpec,
|
|
25
|
-
LexerSpecOptions,
|
|
26
24
|
c,
|
|
27
|
-
createLexer,
|
|
28
25
|
h,
|
|
29
|
-
monokai,
|
|
30
|
-
nanolightTs,
|
|
31
26
|
rwd,
|
|
32
27
|
s,
|
|
33
28
|
svgUse,
|
|
@@ -42,20 +37,10 @@ import {
|
|
|
42
37
|
Tuple argument type for the [`h`](#h) and [`s`](#s) helpers.
|
|
43
38
|
- [`HArgs1`](#HArgs1):
|
|
44
39
|
Single argument type for the [`h`](#h) and [`s`](#s) helpers.
|
|
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
40
|
- [`c`](#c):
|
|
50
41
|
A minimal CSS-in-JS helper that converts a JavaScript object hierarchy into a CSS string.
|
|
51
|
-
- [`createLexer`](#createLexer):
|
|
52
|
-
A helper for building simple stateful lexers (see also [`nanolightTs`](#nanolightTs)).
|
|
53
42
|
- [`h`](#h):
|
|
54
43
|
A lightweight _HyperScript_-style helper for creating and modifying `HTMLElement`s (see also [`s`](#s)).
|
|
55
|
-
- [`monokai`](#monokai):
|
|
56
|
-
A Monokai-inspired color scheme for use with the [`c`](#c) helper and [`nanolightTs`](#nanolightTs) lexer.
|
|
57
|
-
- [`nanolightTs`](#nanolightTs):
|
|
58
|
-
A TypeScript/JavaScript syntax highlighting lexer built using [`createLexer`](#createLexer).
|
|
59
44
|
- [`rwd`](#rwd):
|
|
60
45
|
A responsive web design helper that generates CSS rules for a grid-like layout of floating elements.
|
|
61
46
|
- [`s`](#s):
|
|
@@ -89,38 +74,6 @@ type HArgs1 = Record<PropertyKey, unknown> | null | undefined | Node | string |
|
|
|
89
74
|
|
|
90
75
|
Single argument type for the [`h`](#h) and [`s`](#s) helpers.
|
|
91
76
|
|
|
92
|
-
### LexerSpec
|
|
93
|
-
|
|
94
|
-
```ts
|
|
95
|
-
type LexerSpec<M> = [metadata: M, pattern: string | RegExp, options?: LexerSpecOptions]
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
A [`createLexer`](#createLexer) spec: a `[metadata, pattern, options?]` tuple.
|
|
99
|
-
|
|
100
|
-
### LexerSpecOptions
|
|
101
|
-
|
|
102
|
-
```ts
|
|
103
|
-
type LexerSpecOptions = {
|
|
104
|
-
in?: string | string[]
|
|
105
|
-
pop?: boolean
|
|
106
|
-
push?: string
|
|
107
|
-
}
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
Options of a [`createLexer`](#createLexer) spec controlling the lexer state stack.
|
|
111
|
-
|
|
112
|
-
#### in
|
|
113
|
-
|
|
114
|
-
State(s) in which the spec is active (default: `'root'`).
|
|
115
|
-
|
|
116
|
-
#### pop
|
|
117
|
-
|
|
118
|
-
Whether to pop the current state off the stack after a match (the `'root'` state at the bottom is never popped).
|
|
119
|
-
|
|
120
|
-
#### push
|
|
121
|
-
|
|
122
|
-
State to push onto the stack after a match (applied after `pop`).
|
|
123
|
-
|
|
124
77
|
### c
|
|
125
78
|
|
|
126
79
|
```ts
|
|
@@ -149,6 +102,8 @@ A CSS string representing the compiled rules.
|
|
|
149
102
|
|
|
150
103
|
#### Usage Examples
|
|
151
104
|
|
|
105
|
+
Nested selectors split a rule while preserving property order:
|
|
106
|
+
|
|
152
107
|
```ts
|
|
153
108
|
const actual = c({
|
|
154
109
|
a: {
|
|
@@ -176,6 +131,8 @@ const expected = `
|
|
|
176
131
|
expect(actual).toBe(expected)
|
|
177
132
|
```
|
|
178
133
|
|
|
134
|
+
Selectors can be nested at any depth:
|
|
135
|
+
|
|
179
136
|
```ts
|
|
180
137
|
const actual = c({
|
|
181
138
|
a: {
|
|
@@ -205,6 +162,8 @@ const expected = `
|
|
|
205
162
|
expect(actual).toBe(expected)
|
|
206
163
|
```
|
|
207
164
|
|
|
165
|
+
At-rules, keyframes, media queries and repeated keys with the `$$` suffix:
|
|
166
|
+
|
|
208
167
|
```ts
|
|
209
168
|
const actual = c({
|
|
210
169
|
'@font-face$$1': {
|
|
@@ -280,6 +239,8 @@ const expected = `
|
|
|
280
239
|
expect(actual).toBe(expected)
|
|
281
240
|
```
|
|
282
241
|
|
|
242
|
+
Comma-separated nested selectors are expanded:
|
|
243
|
+
|
|
283
244
|
```ts
|
|
284
245
|
const actual = c({
|
|
285
246
|
a: {
|
|
@@ -304,6 +265,8 @@ const expected = `
|
|
|
304
265
|
expect(actual).toBe(expected)
|
|
305
266
|
```
|
|
306
267
|
|
|
268
|
+
Comma-separated top-level selectors are expanded in nested rules:
|
|
269
|
+
|
|
307
270
|
```ts
|
|
308
271
|
const actual = c({
|
|
309
272
|
'.b,.c': {
|
|
@@ -326,6 +289,8 @@ const expected = `
|
|
|
326
289
|
expect(actual).toBe(expected)
|
|
327
290
|
```
|
|
328
291
|
|
|
292
|
+
Comma-separated selectors at both levels produce all combinations:
|
|
293
|
+
|
|
329
294
|
```ts
|
|
330
295
|
const actual = c({
|
|
331
296
|
'.a,.b': {
|
|
@@ -348,6 +313,8 @@ const expected = `
|
|
|
348
313
|
expect(actual).toBe(expected)
|
|
349
314
|
```
|
|
350
315
|
|
|
316
|
+
CSS custom properties from nested `--` objects:
|
|
317
|
+
|
|
351
318
|
```ts
|
|
352
319
|
const actual = c({
|
|
353
320
|
':root$$monokai': {
|
|
@@ -438,182 +405,6 @@ const expected = `
|
|
|
438
405
|
expect(actual).toBe(expected)
|
|
439
406
|
```
|
|
440
407
|
|
|
441
|
-
### createLexer
|
|
442
|
-
|
|
443
|
-
```ts
|
|
444
|
-
const createLexer: <M, T>(
|
|
445
|
-
decorator: (chunk: string, metadata?: M) => T,
|
|
446
|
-
...specs: LexerSpec<M>[]
|
|
447
|
-
) => (code: string) => T[]
|
|
448
|
-
```
|
|
449
|
-
|
|
450
|
-
A helper for building simple stateful lexers (see also [`nanolightTs`](#nanolightTs)).
|
|
451
|
-
|
|
452
|
-
#### Remarks
|
|
453
|
-
|
|
454
|
-
1. Matches starting at an earlier position take precedence.
|
|
455
|
-
2. Among matches at the same position, the longer one wins.
|
|
456
|
-
3. Among matches of the same position and length, the one defined earlier wins.
|
|
457
|
-
4. Only specs active in the current state (the top of the state stack, initially `'root'`) are considered.
|
|
458
|
-
5. Patterns are matched against the whole input, so `^`, `$`, `\b` and lookbehinds see the text around the current position; a sticky (`y`) `RegExp` matches only at the current position.
|
|
459
|
-
6. Empty matches are ignored.
|
|
460
|
-
|
|
461
|
-
#### decorator
|
|
462
|
-
|
|
463
|
-
A function that wraps each chunk of the input.
|
|
464
|
-
It receives the matched text (`chunk`) and the `metadata` of the spec whose pattern produced the match.
|
|
465
|
-
For unmatched text between matches, `metadata` is `undefined`.
|
|
466
|
-
|
|
467
|
-
#### specs
|
|
468
|
-
|
|
469
|
-
An array of tuples `[metadata, pattern, options?]` where:
|
|
470
|
-
|
|
471
|
-
- `metadata`: arbitrary data (e.g., a CSS class name) passed to `decorator` when the pattern matches.
|
|
472
|
-
- `pattern`: a `string` (matched literally) or `RegExp` to match against the input.
|
|
473
|
-
- `options`: optional [`LexerSpecOptions`](#LexerSpecOptions) (states in which the spec is active and state transitions).
|
|
474
|
-
|
|
475
|
-
#### Returns
|
|
476
|
-
|
|
477
|
-
A lexer function that accepts an input string and returns an array of decorated chunks covering the whole input (in order).
|
|
478
|
-
|
|
479
|
-
#### Usage Examples
|
|
480
|
-
|
|
481
|
-
```ts
|
|
482
|
-
const lexer = createLexer(
|
|
483
|
-
(chunk, metadata) => ({ chunk, metadata }),
|
|
484
|
-
['keyword', /\b(if|else|for)\b/],
|
|
485
|
-
['string', /"[^"]*"/]
|
|
486
|
-
)
|
|
487
|
-
|
|
488
|
-
const result = lexer('if "hello" else "world"')
|
|
489
|
-
|
|
490
|
-
expect(result).toEqual([
|
|
491
|
-
{ chunk: 'if', metadata: 'keyword' },
|
|
492
|
-
{ chunk: ' ', metadata: undefined },
|
|
493
|
-
{ chunk: '"hello"', metadata: 'string' },
|
|
494
|
-
{ chunk: ' ', metadata: undefined },
|
|
495
|
-
{ chunk: 'else', metadata: 'keyword' },
|
|
496
|
-
{ chunk: ' ', metadata: undefined },
|
|
497
|
-
{ chunk: '"world"', metadata: 'string' },
|
|
498
|
-
])
|
|
499
|
-
```
|
|
500
|
-
|
|
501
|
-
```ts
|
|
502
|
-
const lexer = createLexer((chunk, metadata) => `${metadata}:${chunk}`, ['tag', 'BEGIN'], ['end', 'END'])
|
|
503
|
-
|
|
504
|
-
const result = lexer('aBEGINbENDc')
|
|
505
|
-
|
|
506
|
-
expect(result).toEqual(['undefined:a', 'tag:BEGIN', 'undefined:b', 'end:END', 'undefined:c'])
|
|
507
|
-
```
|
|
508
|
-
|
|
509
|
-
```ts
|
|
510
|
-
const lexer = createLexer(chunk => chunk, ['test', /test/])
|
|
511
|
-
|
|
512
|
-
const result = lexer('')
|
|
513
|
-
|
|
514
|
-
expect(result).toEqual([])
|
|
515
|
-
```
|
|
516
|
-
|
|
517
|
-
```ts
|
|
518
|
-
const lexer = createLexer((chunk, metadata) => ({ chunk, metadata }), ['start', /^test/])
|
|
519
|
-
|
|
520
|
-
const result = lexer('test here')
|
|
521
|
-
|
|
522
|
-
expect(result).toEqual([
|
|
523
|
-
{ chunk: 'test', metadata: 'start' },
|
|
524
|
-
{ chunk: ' here', metadata: undefined },
|
|
525
|
-
])
|
|
526
|
-
```
|
|
527
|
-
|
|
528
|
-
```ts
|
|
529
|
-
const lexer = createLexer((chunk, metadata) => metadata, ['later', /x/], ['earlier', /y/])
|
|
530
|
-
|
|
531
|
-
const result = lexer('yx')
|
|
532
|
-
|
|
533
|
-
expect(result).toEqual(['earlier', 'later'])
|
|
534
|
-
```
|
|
535
|
-
|
|
536
|
-
```ts
|
|
537
|
-
const lexer = createLexer(chunk => chunk, ['short', 'a'], ['long', 'abc'])
|
|
538
|
-
|
|
539
|
-
const result = lexer('abc')
|
|
540
|
-
|
|
541
|
-
expect(result).toEqual(['abc'])
|
|
542
|
-
```
|
|
543
|
-
|
|
544
|
-
```ts
|
|
545
|
-
const lexer = createLexer(chunk => chunk, ['empty', ''], ['word', /\w+/])
|
|
546
|
-
|
|
547
|
-
const result = lexer('hello')
|
|
548
|
-
|
|
549
|
-
expect(result).toEqual(['hello'])
|
|
550
|
-
```
|
|
551
|
-
|
|
552
|
-
```ts
|
|
553
|
-
const lexer = createLexer(chunk => chunk, ['test', /xyz/])
|
|
554
|
-
|
|
555
|
-
const result = lexer('abc')
|
|
556
|
-
|
|
557
|
-
expect(result).toEqual(['abc'])
|
|
558
|
-
```
|
|
559
|
-
|
|
560
|
-
```ts
|
|
561
|
-
const lexer = createLexer((_chunk, metadata) => metadata, ['a', 'a'], ['b', 'b'])
|
|
562
|
-
|
|
563
|
-
const result = lexer('aabb')
|
|
564
|
-
|
|
565
|
-
expect(result).toEqual(['a', 'a', 'b', 'b'])
|
|
566
|
-
```
|
|
567
|
-
|
|
568
|
-
```ts
|
|
569
|
-
const lexer = createLexer(
|
|
570
|
-
(chunk, metadata) => `${metadata}:${chunk}`,
|
|
571
|
-
['open', '(', { push: 'inner' }],
|
|
572
|
-
['word', /\w+/, { in: 'inner' }],
|
|
573
|
-
['close', ')', { in: 'inner', pop: true }]
|
|
574
|
-
)
|
|
575
|
-
|
|
576
|
-
const result = lexer('a(b)c)')
|
|
577
|
-
|
|
578
|
-
expect(result).toEqual(['undefined:a', 'open:(', 'word:b', 'close:)', 'undefined:c)'])
|
|
579
|
-
```
|
|
580
|
-
|
|
581
|
-
```ts
|
|
582
|
-
const lexer = createLexer(
|
|
583
|
-
(chunk, metadata) => `${metadata}:${chunk}`,
|
|
584
|
-
['open', '{', { in: ['root', 'nested'], push: 'nested' }],
|
|
585
|
-
['close', '}', { in: 'nested', pop: true }]
|
|
586
|
-
)
|
|
587
|
-
|
|
588
|
-
const result = lexer('{{}}}')
|
|
589
|
-
|
|
590
|
-
expect(result).toEqual(['open:{', 'open:{', 'close:}', 'close:}', 'undefined:}'])
|
|
591
|
-
```
|
|
592
|
-
|
|
593
|
-
```ts
|
|
594
|
-
const lexer = createLexer((chunk, metadata) => `${metadata}:${chunk}`, ['a', 'a'], ['b', /(?<=a)b/])
|
|
595
|
-
|
|
596
|
-
const result = lexer('abcb')
|
|
597
|
-
|
|
598
|
-
expect(result).toEqual(['a:a', 'b:b', 'undefined:cb'])
|
|
599
|
-
```
|
|
600
|
-
|
|
601
|
-
```ts
|
|
602
|
-
const lexer = createLexer((chunk, metadata) => `${metadata}:${chunk}`, ['x', /x/y], ['y', 'y'])
|
|
603
|
-
|
|
604
|
-
const result = lexer('xyx.x')
|
|
605
|
-
|
|
606
|
-
expect(result).toEqual(['x:x', 'y:y', 'x:x', 'undefined:.x'])
|
|
607
|
-
```
|
|
608
|
-
|
|
609
|
-
```ts
|
|
610
|
-
const lexer = createLexer((chunk, metadata) => `${metadata}:${chunk}`, ['digits', /\d*/])
|
|
611
|
-
|
|
612
|
-
const result = lexer('ab12')
|
|
613
|
-
|
|
614
|
-
expect(result).toEqual(['undefined:ab', 'digits:12'])
|
|
615
|
-
```
|
|
616
|
-
|
|
617
408
|
### h
|
|
618
409
|
|
|
619
410
|
```ts
|
|
@@ -650,6 +441,8 @@ The created `HTMLElement` or the modified node.
|
|
|
650
441
|
|
|
651
442
|
#### Usage Examples
|
|
652
443
|
|
|
444
|
+
Creating elements, appending children and setting properties:
|
|
445
|
+
|
|
653
446
|
```ts
|
|
654
447
|
const b = h('b')
|
|
655
448
|
|
|
@@ -670,6 +463,8 @@ expect(i.outerHTML).toBe('<i class="some class">text</i>')
|
|
|
670
463
|
expect(b.outerHTML).toBe('<b><i class="some class">text</i></b>')
|
|
671
464
|
```
|
|
672
465
|
|
|
466
|
+
Text content from strings, numbers and `$innerText`:
|
|
467
|
+
|
|
673
468
|
```ts
|
|
674
469
|
expect(h('span', 'text').outerHTML).toBe('<span>text</span>')
|
|
675
470
|
|
|
@@ -680,6 +475,8 @@ expect(h('span', '42').outerHTML).toBe('<span>42</span>')
|
|
|
680
475
|
expect(h('span', 42).outerHTML).toBe('<span>42</span>')
|
|
681
476
|
```
|
|
682
477
|
|
|
478
|
+
`style` attribute vs `$style` property (string or object):
|
|
479
|
+
|
|
683
480
|
```ts
|
|
684
481
|
expect(h('div', { style: 'margin:0;padding:0' }).outerHTML).toBe('<div style="margin:0;padding:0"></div>')
|
|
685
482
|
|
|
@@ -690,6 +487,8 @@ expect(h('div', { $style: { margin: 0, padding: 0 } }).outerHTML).toBe(
|
|
|
690
487
|
)
|
|
691
488
|
```
|
|
692
489
|
|
|
490
|
+
`value` attribute vs `$value` property:
|
|
491
|
+
|
|
693
492
|
```ts
|
|
694
493
|
const input1 = h('input', { value: 42 })
|
|
695
494
|
const input2 = h('input', { $value: '42' })
|
|
@@ -701,6 +500,8 @@ expect(input1.outerHTML).toBe('<input value="42">')
|
|
|
701
500
|
expect(input2.outerHTML).toBe('<input>')
|
|
702
501
|
```
|
|
703
502
|
|
|
503
|
+
`checked` attribute vs `$checked` property:
|
|
504
|
+
|
|
704
505
|
```ts
|
|
705
506
|
const checkbox1 = h('input', {
|
|
706
507
|
type: 'checkbox',
|
|
@@ -720,6 +521,8 @@ expect(checkbox1.outerHTML).toBe('<input type="checkbox" checked="">')
|
|
|
720
521
|
expect(checkbox2.outerHTML).toBe('<input type="checkbox">')
|
|
721
522
|
```
|
|
722
523
|
|
|
524
|
+
Object values of `$`-prefixed properties are merged:
|
|
525
|
+
|
|
723
526
|
```ts
|
|
724
527
|
const div = h('div')
|
|
725
528
|
|
|
@@ -734,6 +537,8 @@ h(div, { $key: { two: 2 } })
|
|
|
734
537
|
expect(div.key).toEqual({ one: 1, two: 2 })
|
|
735
538
|
```
|
|
736
539
|
|
|
540
|
+
Class attribute, appending text and nested children given as arrays:
|
|
541
|
+
|
|
737
542
|
```ts
|
|
738
543
|
const elemWithClass = h('div', { class: 'test' })
|
|
739
544
|
|
|
@@ -750,258 +555,6 @@ const elemWithNested = h('div', ['span', 'hello'], ['b', 'world'])
|
|
|
750
555
|
expect(elemWithNested.outerHTML).toBe('<div><span>hello</span><b>world</b></div>')
|
|
751
556
|
```
|
|
752
557
|
|
|
753
|
-
### monokai
|
|
754
|
-
|
|
755
|
-
```ts
|
|
756
|
-
const monokai: CNode
|
|
757
|
-
```
|
|
758
|
-
|
|
759
|
-
A Monokai-inspired color scheme for use with the [`c`](#c) helper and [`nanolightTs`](#nanolightTs) lexer.
|
|
760
|
-
It styles `pre` and `code>span` elements with the class names produced by [`nanolightTs`](#nanolightTs) (with light, dark and print variants).
|
|
761
|
-
|
|
762
|
-
#### Usage Examples
|
|
763
|
-
|
|
764
|
-
```ts
|
|
765
|
-
const style = h('style', c(monokai))
|
|
766
|
-
const pre = h('pre', ['code', ...nanolightTs('const x = 42')])
|
|
767
|
-
|
|
768
|
-
expect(style.textContent).toContain('code>span.keyword1{color:var(--keyword1)}')
|
|
769
|
-
expect(style.textContent).toContain('@media only screen and (prefers-color-scheme: dark){:root{--bg:#2d2a2e;')
|
|
770
|
-
expect(pre.outerHTML).toBe(
|
|
771
|
-
'<pre><code>' +
|
|
772
|
-
'<span class="keyword1">const</span> ' +
|
|
773
|
-
'<span class="identifier4">x</span> ' +
|
|
774
|
-
'<span class="operator">=</span> ' +
|
|
775
|
-
'<span class="number">42</span>' +
|
|
776
|
-
'</code></pre>'
|
|
777
|
-
)
|
|
778
|
-
```
|
|
779
|
-
|
|
780
|
-
### nanolightTs
|
|
781
|
-
|
|
782
|
-
```ts
|
|
783
|
-
const nanolightTs: (code: string) => HArgs1[]
|
|
784
|
-
```
|
|
785
|
-
|
|
786
|
-
A TypeScript/JavaScript syntax highlighting lexer built using [`createLexer`](#createLexer).
|
|
787
|
-
|
|
788
|
-
#### code
|
|
789
|
-
|
|
790
|
-
The source code string to tokenize.
|
|
791
|
-
|
|
792
|
-
#### Returns
|
|
793
|
-
|
|
794
|
-
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)).
|
|
795
|
-
|
|
796
|
-
#### Usage Examples
|
|
797
|
-
|
|
798
|
-
```ts
|
|
799
|
-
const codeJs = 'const answerToLifeTheUniverseAndEverything = ' + "{ 42: 42 }['42'] /* 42 */"
|
|
800
|
-
|
|
801
|
-
expect(nanolightTs(codeJs)).toEqual([
|
|
802
|
-
['span', { class: 'keyword1' }, 'const'],
|
|
803
|
-
' ',
|
|
804
|
-
['span', { class: 'identifier4' }, 'answerToLifeTheUniverseAndEverything'],
|
|
805
|
-
' ',
|
|
806
|
-
['span', { class: 'operator' }, '='],
|
|
807
|
-
' ',
|
|
808
|
-
['span', { class: 'punctuation' }, '{'],
|
|
809
|
-
' ',
|
|
810
|
-
['span', { class: 'number' }, '42'],
|
|
811
|
-
['span', { class: 'operator' }, ':'],
|
|
812
|
-
' ',
|
|
813
|
-
['span', { class: 'number' }, '42'],
|
|
814
|
-
' ',
|
|
815
|
-
['span', { class: 'punctuation' }, '}'],
|
|
816
|
-
['span', { class: 'punctuation' }, '['],
|
|
817
|
-
['span', { class: 'string' }, "'42'"],
|
|
818
|
-
['span', { class: 'punctuation' }, ']'],
|
|
819
|
-
' ',
|
|
820
|
-
['span', { class: 'comment' }, '/* 42 */'],
|
|
821
|
-
])
|
|
822
|
-
```
|
|
823
|
-
|
|
824
|
-
```ts
|
|
825
|
-
const codeJs = 'const x = 42 // answer'
|
|
826
|
-
|
|
827
|
-
expect(nanolightTs(codeJs)).toEqual([
|
|
828
|
-
['span', { class: 'keyword1' }, 'const'],
|
|
829
|
-
' ',
|
|
830
|
-
['span', { class: 'identifier4' }, 'x'],
|
|
831
|
-
' ',
|
|
832
|
-
['span', { class: 'operator' }, '='],
|
|
833
|
-
' ',
|
|
834
|
-
['span', { class: 'number' }, '42'],
|
|
835
|
-
' ',
|
|
836
|
-
['span', { class: 'comment' }, '// answer'],
|
|
837
|
-
])
|
|
838
|
-
```
|
|
839
|
-
|
|
840
|
-
```ts
|
|
841
|
-
expect(nanolightTs('x = /a\\/b[/]c/gi.test(y) / 2')).toEqual([
|
|
842
|
-
['span', { class: 'identifier4' }, 'x'],
|
|
843
|
-
' ',
|
|
844
|
-
['span', { class: 'operator' }, '='],
|
|
845
|
-
' ',
|
|
846
|
-
['span', { class: 'string' }, '/a\\/b[/]c/gi'],
|
|
847
|
-
['span', { class: 'punctuation' }, '.'],
|
|
848
|
-
['span', { class: 'identifier2' }, 'test'],
|
|
849
|
-
['span', { class: 'punctuation' }, '('],
|
|
850
|
-
['span', { class: 'identifier4' }, 'y'],
|
|
851
|
-
['span', { class: 'punctuation' }, ')'],
|
|
852
|
-
' ',
|
|
853
|
-
['span', { class: 'operator' }, '/'],
|
|
854
|
-
' ',
|
|
855
|
-
['span', { class: 'number' }, '2'],
|
|
856
|
-
])
|
|
857
|
-
|
|
858
|
-
expect(nanolightTs('a / b / c')).toEqual([
|
|
859
|
-
['span', { class: 'identifier4' }, 'a'],
|
|
860
|
-
' ',
|
|
861
|
-
['span', { class: 'operator' }, '/'],
|
|
862
|
-
' ',
|
|
863
|
-
['span', { class: 'identifier4' }, 'b'],
|
|
864
|
-
' ',
|
|
865
|
-
['span', { class: 'operator' }, '/'],
|
|
866
|
-
' ',
|
|
867
|
-
['span', { class: 'identifier4' }, 'c'],
|
|
868
|
-
])
|
|
869
|
-
|
|
870
|
-
expect(nanolightTs('return / x /')).toEqual([
|
|
871
|
-
['span', { class: 'keyword1' }, 'return'],
|
|
872
|
-
' ',
|
|
873
|
-
['span', { class: 'string' }, '/ x /'],
|
|
874
|
-
])
|
|
875
|
-
```
|
|
876
|
-
|
|
877
|
-
```ts
|
|
878
|
-
expect(nanolightTs('`a ${b + `c ${ {d}.d }`} \\${e} $f`')).toEqual([
|
|
879
|
-
['span', { class: 'string' }, '`a '],
|
|
880
|
-
['span', { class: 'punctuation' }, '${'],
|
|
881
|
-
['span', { class: 'identifier4' }, 'b'],
|
|
882
|
-
' ',
|
|
883
|
-
['span', { class: 'operator' }, '+'],
|
|
884
|
-
' ',
|
|
885
|
-
['span', { class: 'string' }, '`c '],
|
|
886
|
-
['span', { class: 'punctuation' }, '${'],
|
|
887
|
-
' ',
|
|
888
|
-
['span', { class: 'punctuation' }, '{'],
|
|
889
|
-
['span', { class: 'identifier4' }, 'd'],
|
|
890
|
-
['span', { class: 'punctuation' }, '}'],
|
|
891
|
-
['span', { class: 'punctuation' }, '.'],
|
|
892
|
-
['span', { class: 'identifier4' }, 'd'],
|
|
893
|
-
' ',
|
|
894
|
-
['span', { class: 'punctuation' }, '}'],
|
|
895
|
-
['span', { class: 'string' }, '`'],
|
|
896
|
-
['span', { class: 'punctuation' }, '}'],
|
|
897
|
-
['span', { class: 'string' }, ' \\${e} $f`'],
|
|
898
|
-
])
|
|
899
|
-
|
|
900
|
-
expect(nanolightTs('`${x}` / 2')).toEqual([
|
|
901
|
-
['span', { class: 'string' }, '`'],
|
|
902
|
-
['span', { class: 'punctuation' }, '${'],
|
|
903
|
-
['span', { class: 'identifier4' }, 'x'],
|
|
904
|
-
['span', { class: 'punctuation' }, '}'],
|
|
905
|
-
['span', { class: 'string' }, '`'],
|
|
906
|
-
' ',
|
|
907
|
-
['span', { class: 'operator' }, '/'],
|
|
908
|
-
' ',
|
|
909
|
-
['span', { class: 'number' }, '2'],
|
|
910
|
-
])
|
|
911
|
-
```
|
|
912
|
-
|
|
913
|
-
```ts
|
|
914
|
-
expect(nanolightTs('"a // b" // c')).toEqual([
|
|
915
|
-
['span', { class: 'string' }, '"a // b"'],
|
|
916
|
-
' ',
|
|
917
|
-
['span', { class: 'comment' }, '// c'],
|
|
918
|
-
])
|
|
919
|
-
|
|
920
|
-
expect(nanolightTs("'it\\'s' /* x")).toEqual([
|
|
921
|
-
['span', { class: 'string' }, "'it\\'s'"],
|
|
922
|
-
' ',
|
|
923
|
-
['span', { class: 'comment' }, '/* x'],
|
|
924
|
-
])
|
|
925
|
-
```
|
|
926
|
-
|
|
927
|
-
```ts
|
|
928
|
-
expect(nanolightTs('0xFF + 1_000n + .5e-3')).toEqual([
|
|
929
|
-
['span', { class: 'number' }, '0xFF'],
|
|
930
|
-
' ',
|
|
931
|
-
['span', { class: 'operator' }, '+'],
|
|
932
|
-
' ',
|
|
933
|
-
['span', { class: 'number' }, '1_000n'],
|
|
934
|
-
' ',
|
|
935
|
-
['span', { class: 'operator' }, '+'],
|
|
936
|
-
' ',
|
|
937
|
-
['span', { class: 'number' }, '.5e-3'],
|
|
938
|
-
])
|
|
939
|
-
```
|
|
940
|
-
|
|
941
|
-
```ts
|
|
942
|
-
expect(nanolightTs('a++ / 2 / b')).toEqual([
|
|
943
|
-
['span', { class: 'identifier4' }, 'a'],
|
|
944
|
-
['span', { class: 'operator' }, '++'],
|
|
945
|
-
' ',
|
|
946
|
-
['span', { class: 'operator' }, '/'],
|
|
947
|
-
' ',
|
|
948
|
-
['span', { class: 'number' }, '2'],
|
|
949
|
-
' ',
|
|
950
|
-
['span', { class: 'operator' }, '/'],
|
|
951
|
-
' ',
|
|
952
|
-
['span', { class: 'identifier4' }, 'b'],
|
|
953
|
-
])
|
|
954
|
-
|
|
955
|
-
expect(nanolightTs('x?.5:a?.b')).toEqual([
|
|
956
|
-
['span', { class: 'identifier4' }, 'x'],
|
|
957
|
-
['span', { class: 'operator' }, '?'],
|
|
958
|
-
['span', { class: 'number' }, '.5'],
|
|
959
|
-
['span', { class: 'operator' }, ':'],
|
|
960
|
-
['span', { class: 'identifier4' }, 'a'],
|
|
961
|
-
['span', { class: 'punctuation' }, '?.'],
|
|
962
|
-
['span', { class: 'identifier4' }, 'b'],
|
|
963
|
-
])
|
|
964
|
-
```
|
|
965
|
-
|
|
966
|
-
```ts
|
|
967
|
-
expect(nanolightTs('p.catch(obj.in, ...this)')).toEqual([
|
|
968
|
-
['span', { class: 'identifier4' }, 'p'],
|
|
969
|
-
['span', { class: 'punctuation' }, '.'],
|
|
970
|
-
['span', { class: 'identifier2' }, 'catch'],
|
|
971
|
-
['span', { class: 'punctuation' }, '('],
|
|
972
|
-
['span', { class: 'identifier4' }, 'obj'],
|
|
973
|
-
['span', { class: 'punctuation' }, '.'],
|
|
974
|
-
['span', { class: 'identifier4' }, 'in'],
|
|
975
|
-
['span', { class: 'punctuation' }, ','],
|
|
976
|
-
' ',
|
|
977
|
-
['span', { class: 'operator' }, '...'],
|
|
978
|
-
['span', { class: 'keyword1' }, 'this'],
|
|
979
|
-
['span', { class: 'punctuation' }, ')'],
|
|
980
|
-
])
|
|
981
|
-
```
|
|
982
|
-
|
|
983
|
-
```ts
|
|
984
|
-
expect(nanolightTs('#!/usr/bin/env bun\n@sealed class A { #x = 1 }')).toEqual([
|
|
985
|
-
['span', { class: 'comment' }, '#!/usr/bin/env bun'],
|
|
986
|
-
'\n',
|
|
987
|
-
['span', { class: 'identifier2' }, '@sealed'],
|
|
988
|
-
' ',
|
|
989
|
-
['span', { class: 'keyword1' }, 'class'],
|
|
990
|
-
' ',
|
|
991
|
-
['span', { class: 'identifier1' }, 'A'],
|
|
992
|
-
' ',
|
|
993
|
-
['span', { class: 'punctuation' }, '{'],
|
|
994
|
-
' ',
|
|
995
|
-
['span', { class: 'identifier4' }, '#x'],
|
|
996
|
-
' ',
|
|
997
|
-
['span', { class: 'operator' }, '='],
|
|
998
|
-
' ',
|
|
999
|
-
['span', { class: 'number' }, '1'],
|
|
1000
|
-
' ',
|
|
1001
|
-
['span', { class: 'punctuation' }, '}'],
|
|
1002
|
-
])
|
|
1003
|
-
```
|
|
1004
|
-
|
|
1005
558
|
### rwd
|
|
1006
559
|
|
|
1007
560
|
```ts
|
|
@@ -1043,6 +596,8 @@ An array of breakpoint specifications, each a tuple of:
|
|
|
1043
596
|
|
|
1044
597
|
#### Usage Examples
|
|
1045
598
|
|
|
599
|
+
Responsive grid cell widths and heights with media queries:
|
|
600
|
+
|
|
1046
601
|
```ts
|
|
1047
602
|
const style: CNode = {
|
|
1048
603
|
body: {
|
|
@@ -1124,24 +679,32 @@ The created `SVGElement` or the modified node.
|
|
|
1124
679
|
|
|
1125
680
|
#### Usage Examples
|
|
1126
681
|
|
|
682
|
+
Namespaced `xlink:` attribute set to `true` gets an empty value:
|
|
683
|
+
|
|
1127
684
|
```ts
|
|
1128
685
|
const svg = s('svg', { 'xlink:href': true })
|
|
1129
686
|
|
|
1130
687
|
expect(svg.getAttributeNS('http://www.w3.org/1999/xlink', 'href')).toBe('')
|
|
1131
688
|
```
|
|
1132
689
|
|
|
690
|
+
Namespaced `xlink:` attribute set to `false` is not set:
|
|
691
|
+
|
|
1133
692
|
```ts
|
|
1134
693
|
const svg = s('svg', { 'xlink:href': false })
|
|
1135
694
|
|
|
1136
695
|
expect(svg.getAttributeNS('http://www.w3.org/1999/xlink', 'href')).toBeNull()
|
|
1137
696
|
```
|
|
1138
697
|
|
|
698
|
+
Namespaced `xlink:` attribute with a string value:
|
|
699
|
+
|
|
1139
700
|
```ts
|
|
1140
701
|
const svg = s('svg', { 'xlink:href': 'http://example.com' })
|
|
1141
702
|
|
|
1142
703
|
expect(svg.getAttributeNS('http://www.w3.org/1999/xlink', 'href')).toBe('http://example.com')
|
|
1143
704
|
```
|
|
1144
705
|
|
|
706
|
+
Namespaced `xlink:` attribute with a number value:
|
|
707
|
+
|
|
1145
708
|
```ts
|
|
1146
709
|
const svg = s('svg', { 'xlink:title': 42 })
|
|
1147
710
|
|
|
@@ -1176,6 +739,8 @@ An `SVGSVGElement` containing a `<use>` element.
|
|
|
1176
739
|
|
|
1177
740
|
#### Usage Examples
|
|
1178
741
|
|
|
742
|
+
SVG `use` element referencing a symbol, with extra attributes:
|
|
743
|
+
|
|
1179
744
|
```ts
|
|
1180
745
|
const svg = svgUse('icon-home')
|
|
1181
746
|
|
package/nnn.d.ts
CHANGED
|
@@ -24,62 +24,6 @@ export type CNode = {
|
|
|
24
24
|
* A CSS string representing the compiled rules.
|
|
25
25
|
*/
|
|
26
26
|
export declare const c: (node: CNode, splitter?: string) => string
|
|
27
|
-
/**
|
|
28
|
-
* Options of a {@link createLexer} spec controlling the lexer state stack.
|
|
29
|
-
*
|
|
30
|
-
* @property in
|
|
31
|
-
*
|
|
32
|
-
* State(s) in which the spec is active (default: `'root'`).
|
|
33
|
-
*
|
|
34
|
-
* @property pop
|
|
35
|
-
*
|
|
36
|
-
* Whether to pop the current state off the stack after a match (the `'root'` state at the bottom is never popped).
|
|
37
|
-
*
|
|
38
|
-
* @property push
|
|
39
|
-
*
|
|
40
|
-
* State to push onto the stack after a match (applied after `pop`).
|
|
41
|
-
*/
|
|
42
|
-
export type LexerSpecOptions = {
|
|
43
|
-
in?: string | string[]
|
|
44
|
-
pop?: boolean
|
|
45
|
-
push?: string
|
|
46
|
-
}
|
|
47
|
-
/** A {@link createLexer} spec: a `[metadata, pattern, options?]` tuple. */
|
|
48
|
-
export type LexerSpec<M> = [metadata: M, pattern: string | RegExp, options?: LexerSpecOptions]
|
|
49
|
-
/**
|
|
50
|
-
* A helper for building simple stateful lexers (see also {@link nanolightTs}).
|
|
51
|
-
*
|
|
52
|
-
* @remarks
|
|
53
|
-
*
|
|
54
|
-
* 1. Matches starting at an earlier position take precedence.
|
|
55
|
-
* 2. Among matches at the same position, the longer one wins.
|
|
56
|
-
* 3. Among matches of the same position and length, the one defined earlier wins.
|
|
57
|
-
* 4. Only specs active in the current state (the top of the state stack, initially `'root'`) are considered.
|
|
58
|
-
* 5. Patterns are matched against the whole input, so `^`, `$`, `\b` and lookbehinds see the text around the current position; a sticky (`y`) `RegExp` matches only at the current position.
|
|
59
|
-
* 6. Empty matches are ignored.
|
|
60
|
-
*
|
|
61
|
-
* @param decorator
|
|
62
|
-
*
|
|
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`.
|
|
66
|
-
*
|
|
67
|
-
* @param specs
|
|
68
|
-
*
|
|
69
|
-
* An array of tuples `[metadata, pattern, options?]` where:
|
|
70
|
-
*
|
|
71
|
-
* - `metadata`: arbitrary data (e.g., a CSS class name) passed to `decorator` when the pattern matches.
|
|
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).
|
|
74
|
-
*
|
|
75
|
-
* @returns
|
|
76
|
-
*
|
|
77
|
-
* A lexer function that accepts an input string and returns an array of decorated chunks covering the whole input (in order).
|
|
78
|
-
*/
|
|
79
|
-
export declare const createLexer: <M, T>(
|
|
80
|
-
decorator: (chunk: string, metadata?: M) => T,
|
|
81
|
-
...specs: LexerSpec<M>[]
|
|
82
|
-
) => (code: string) => T[]
|
|
83
27
|
/** Single argument type for the {@link h} and {@link s} helpers. */
|
|
84
28
|
export type HArgs1 = Record<PropertyKey, unknown> | null | undefined | Node | string | number | HArgs
|
|
85
29
|
/** Tuple argument type for the {@link h} and {@link s} helpers. */
|
|
@@ -166,23 +110,6 @@ export declare const s: {
|
|
|
166
110
|
* An `SVGSVGElement` containing a `<use>` element.
|
|
167
111
|
*/
|
|
168
112
|
export declare const svgUse: (id: string, ...args: HArgs1[]) => SVGSVGElement
|
|
169
|
-
/**
|
|
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).
|
|
172
|
-
*/
|
|
173
|
-
export declare const monokai: CNode
|
|
174
|
-
/**
|
|
175
|
-
* A TypeScript/JavaScript syntax highlighting lexer built using {@link createLexer}.
|
|
176
|
-
*
|
|
177
|
-
* @param code
|
|
178
|
-
*
|
|
179
|
-
* The source code string to tokenize.
|
|
180
|
-
*
|
|
181
|
-
* @returns
|
|
182
|
-
*
|
|
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}).
|
|
184
|
-
*/
|
|
185
|
-
export declare const nanolightTs: (code: string) => HArgs1[]
|
|
186
113
|
/**
|
|
187
114
|
* A responsive web design helper that generates CSS rules for a grid-like layout of floating elements.
|
|
188
115
|
* It modifies `root` in place: it sets base styles for `selector` and adds a `@media(min-width:…px)` rule for each breakpoint.
|
package/nnn.js
CHANGED
|
@@ -50,79 +50,6 @@ 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/createLexer.ts
|
|
54
|
-
var ROOT = "root";
|
|
55
|
-
var createLexer = (decorator, ...specs) => {
|
|
56
|
-
const compiled = specs.map(([metadata, pattern, options]) => ({
|
|
57
|
-
metadata,
|
|
58
|
-
pattern: pattern instanceof RegExp ? new RegExp(pattern.source, pattern.sticky ? pattern.flags : pattern.flags.replace("g", "") + "g") : pattern,
|
|
59
|
-
states: [options?.in ?? ROOT].flat(),
|
|
60
|
-
pop: options?.pop ?? false,
|
|
61
|
-
push: options?.push
|
|
62
|
-
}));
|
|
63
|
-
const find = (pattern, code, position) => {
|
|
64
|
-
if (typeof pattern === "string") {
|
|
65
|
-
const index = pattern.length > 0 ? code.indexOf(pattern, position) : -1;
|
|
66
|
-
return [index, pattern];
|
|
67
|
-
}
|
|
68
|
-
pattern.lastIndex = position;
|
|
69
|
-
for (let match = pattern.exec(code);match != null; match = pattern.exec(code)) {
|
|
70
|
-
if (match[0].length > 0) {
|
|
71
|
-
return [match.index, match[0]];
|
|
72
|
-
}
|
|
73
|
-
if (pattern.sticky) {
|
|
74
|
-
break;
|
|
75
|
-
}
|
|
76
|
-
pattern.lastIndex = match.index + 1;
|
|
77
|
-
}
|
|
78
|
-
return [-1, ""];
|
|
79
|
-
};
|
|
80
|
-
return (code) => {
|
|
81
|
-
const result = [];
|
|
82
|
-
const stack = [ROOT];
|
|
83
|
-
const cache = compiled.map(() => ({ from: -1, index: -1, chunk: "" }));
|
|
84
|
-
let position = 0;
|
|
85
|
-
while (position < code.length) {
|
|
86
|
-
const state = stack.at(-1);
|
|
87
|
-
let best = -1;
|
|
88
|
-
let bestIndex = Infinity;
|
|
89
|
-
let bestChunk = "";
|
|
90
|
-
compiled.forEach(({ pattern, states }, i) => {
|
|
91
|
-
if (!states.includes(state)) {
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
const cached = cache[i];
|
|
95
|
-
const isValid = cached.from >= 0 && (pattern instanceof RegExp && pattern.sticky ? cached.from === position : cached.from <= position && (cached.index < 0 || cached.index >= position));
|
|
96
|
-
if (!isValid) {
|
|
97
|
-
[cached.index, cached.chunk] = find(pattern, code, position);
|
|
98
|
-
cached.from = position;
|
|
99
|
-
}
|
|
100
|
-
if (cached.index >= 0 && (cached.index < bestIndex || cached.index === bestIndex && cached.chunk.length > bestChunk.length)) {
|
|
101
|
-
best = i;
|
|
102
|
-
bestIndex = cached.index;
|
|
103
|
-
bestChunk = cached.chunk;
|
|
104
|
-
}
|
|
105
|
-
});
|
|
106
|
-
if (best < 0) {
|
|
107
|
-
result.push(decorator(code.slice(position)));
|
|
108
|
-
break;
|
|
109
|
-
}
|
|
110
|
-
const { metadata, pop, push } = compiled[best];
|
|
111
|
-
if (bestIndex > position) {
|
|
112
|
-
result.push(decorator(code.slice(position, bestIndex)));
|
|
113
|
-
}
|
|
114
|
-
result.push(decorator(bestChunk, metadata));
|
|
115
|
-
position = bestIndex + bestChunk.length;
|
|
116
|
-
if (pop && stack.length > 1) {
|
|
117
|
-
stack.pop();
|
|
118
|
-
}
|
|
119
|
-
if (push != null) {
|
|
120
|
-
stack.push(push);
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
return result;
|
|
124
|
-
};
|
|
125
|
-
};
|
|
126
53
|
// src/nnn/h.ts
|
|
127
54
|
var XLINK_NS = "http://www.w3.org/1999/xlink";
|
|
128
55
|
var _h = (createElement, tagOrNode, ...args) => {
|
|
@@ -180,113 +107,6 @@ var _h = (createElement, tagOrNode, ...args) => {
|
|
|
180
107
|
var h = _h.bind(null, (tag) => document.createElement(tag));
|
|
181
108
|
var s = _h.bind(null, (tag) => document.createElementNS("http://www.w3.org/2000/svg", tag));
|
|
182
109
|
var svgUse = (id, ...args) => s("svg", ["use", { "xlink:href": "#" + id }], ...args);
|
|
183
|
-
// src/nnn/monokai.ts
|
|
184
|
-
var monokai = {
|
|
185
|
-
":root$$monokai": {
|
|
186
|
-
"--": {
|
|
187
|
-
bg: "#faf4f2",
|
|
188
|
-
fg: "#29242a",
|
|
189
|
-
comment: "#918c8e",
|
|
190
|
-
identifier: {
|
|
191
|
-
1: "#7058be",
|
|
192
|
-
2: "#269d69",
|
|
193
|
-
3: "#1c8ca8",
|
|
194
|
-
4: "#29242a"
|
|
195
|
-
},
|
|
196
|
-
keyword: {
|
|
197
|
-
1: "#e14775",
|
|
198
|
-
2: "#7058be",
|
|
199
|
-
3: "#1c8ca8"
|
|
200
|
-
},
|
|
201
|
-
number: "#7058be",
|
|
202
|
-
operator: "#e14775",
|
|
203
|
-
punctuation: "#918c8e",
|
|
204
|
-
string: "#cc7a0a"
|
|
205
|
-
}
|
|
206
|
-
},
|
|
207
|
-
pre$$monokai: {
|
|
208
|
-
"background-color": "var(--bg)",
|
|
209
|
-
color: "var(--fg)",
|
|
210
|
-
margin: 0,
|
|
211
|
-
padding: "1em",
|
|
212
|
-
overflow: "visible",
|
|
213
|
-
" code": {
|
|
214
|
-
"font-family": '"Source Code Pro"',
|
|
215
|
-
padding: 0
|
|
216
|
-
}
|
|
217
|
-
},
|
|
218
|
-
"code>span.$$monokai": {
|
|
219
|
-
bg: { color: "var(--bg)" },
|
|
220
|
-
fg: { color: "var(--fg)" },
|
|
221
|
-
comment: { color: "var(--comment)" },
|
|
222
|
-
identifier: {
|
|
223
|
-
1: { color: "var(--identifier1)" },
|
|
224
|
-
2: { color: "var(--identifier2)" },
|
|
225
|
-
3: { color: "var(--identifier3)" },
|
|
226
|
-
4: { color: "var(--identifier4)" }
|
|
227
|
-
},
|
|
228
|
-
keyword: {
|
|
229
|
-
1: { color: "var(--keyword1)" },
|
|
230
|
-
2: { color: "var(--keyword2)" },
|
|
231
|
-
3: { color: "var(--keyword3)" }
|
|
232
|
-
},
|
|
233
|
-
number: { color: "var(--number)" },
|
|
234
|
-
operator: { color: "var(--operator)" },
|
|
235
|
-
punctuation: { color: "var(--punctuation)" },
|
|
236
|
-
string: { color: "var(--string)" }
|
|
237
|
-
},
|
|
238
|
-
"@media only screen and (prefers-color-scheme: dark)$$monokai": {
|
|
239
|
-
":root": {
|
|
240
|
-
"--": {
|
|
241
|
-
bg: "#2d2a2e",
|
|
242
|
-
fg: "#fcfcfa",
|
|
243
|
-
comment: "#727072",
|
|
244
|
-
identifier: {
|
|
245
|
-
1: "#ae81ff",
|
|
246
|
-
2: "#a9dc76",
|
|
247
|
-
3: "#66d9ef",
|
|
248
|
-
4: "#fcfcfa"
|
|
249
|
-
},
|
|
250
|
-
keyword: {
|
|
251
|
-
1: "#ff6188",
|
|
252
|
-
2: "#ae81ff",
|
|
253
|
-
3: "#66d9ef"
|
|
254
|
-
},
|
|
255
|
-
number: "#ae81ff",
|
|
256
|
-
operator: "#ff6188",
|
|
257
|
-
punctuation: "#727072",
|
|
258
|
-
string: "#ffd866"
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
},
|
|
262
|
-
"@media print$$monokai": {
|
|
263
|
-
pre: {
|
|
264
|
-
"background-color": "white"
|
|
265
|
-
},
|
|
266
|
-
":root": {
|
|
267
|
-
"--bg": "white"
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
};
|
|
271
|
-
// src/nnn/nanolightTs.ts
|
|
272
|
-
var COMMENT = "comment";
|
|
273
|
-
var IDENTIFIER1 = "identifier1";
|
|
274
|
-
var IDENTIFIER2 = "identifier2";
|
|
275
|
-
var IDENTIFIER3 = "identifier3";
|
|
276
|
-
var IDENTIFIER4 = "identifier4";
|
|
277
|
-
var KEYWORD1 = "keyword1";
|
|
278
|
-
var KEYWORD2 = "keyword2";
|
|
279
|
-
var KEYWORD3 = "keyword3";
|
|
280
|
-
var NUMBER = "number";
|
|
281
|
-
var OPERATOR = "operator";
|
|
282
|
-
var PUNCTUATION = "punctuation";
|
|
283
|
-
var STRING = "string";
|
|
284
|
-
var ROOT2 = "root";
|
|
285
|
-
var TEMPLATE = "template";
|
|
286
|
-
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 }], [
|
|
287
|
-
STRING,
|
|
288
|
-
/(?<=(?:^|[!%&(*,:;<=>?[^{|}~]|(?<!\+)\+|(?<!-)-|(?<!\.)\b(?:await|case|delete|do|else|in|instanceof|new|of|return|throw|typeof|void|yield))\s*)\/(?![*/])(?:[^\\/[\n]|\\.|\[(?:[^\\\]\n]|\\.)*\]?)+\/[dgimsuvy]*/
|
|
289
|
-
], [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
110
|
// src/nnn/rwd.ts
|
|
291
111
|
var rwd = (root, selector, cellWidthPx, cellHeightPx, ...specs) => {
|
|
292
112
|
const main = root[selector] ??= {};
|
|
@@ -313,10 +133,7 @@ var rwd = (root, selector, cellWidthPx, cellHeightPx, ...specs) => {
|
|
|
313
133
|
};
|
|
314
134
|
export {
|
|
315
135
|
c,
|
|
316
|
-
createLexer,
|
|
317
136
|
h,
|
|
318
|
-
monokai,
|
|
319
|
-
nanolightTs,
|
|
320
137
|
rwd,
|
|
321
138
|
s,
|
|
322
139
|
svgUse
|
package/package.json
CHANGED
|
@@ -1,20 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jackens/nnn",
|
|
3
|
-
"version": "2026.
|
|
3
|
+
"version": "2026.10.1",
|
|
4
4
|
"description": "Jackens’ JavaScript helpers.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"CSS-in-JS",
|
|
7
7
|
"HyperScript",
|
|
8
8
|
"JS-to-CSS",
|
|
9
|
-
"Monokai",
|
|
10
|
-
"RWD",
|
|
11
|
-
"Responsive Web Design",
|
|
12
|
-
"SVG",
|
|
13
|
-
"highlight",
|
|
14
|
-
"lexer",
|
|
15
|
-
"nanolight",
|
|
16
9
|
"nnn",
|
|
17
|
-
"
|
|
10
|
+
"Responsive Web Design",
|
|
11
|
+
"RWD"
|
|
18
12
|
],
|
|
19
13
|
"license": "MIT",
|
|
20
14
|
"author": "Jackens",
|