@jackens/nnn 2026.4.14 → 2026.4.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/readme.md +226 -189
package/package.json CHANGED
@@ -44,5 +44,5 @@
44
44
  "name": "@jackens/nnn",
45
45
  "type": "module",
46
46
  "types": "nnn.d.ts",
47
- "version": "2026.4.14"
47
+ "version": "2026.4.15"
48
48
  }
package/readme.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # nnn
2
2
 
3
- A collection of Jackens’ JavaScript helper utilities (version: `2026.4.14`).
3
+ A collection of Jackens’ JavaScript helper utilities (version: `2026.4.15`).
4
4
 
5
5
  ## Installation
6
6
 
@@ -19,30 +19,29 @@ npm i @jackens/nnn
19
19
  ```js
20
20
  import {
21
21
  c,
22
- csvParse,
23
- fixPlTypography,
24
- h,
25
- hasOwn,
26
- isArray,
27
- isFiniteNumber,
28
- isInteger,
29
- isNumber,
30
- isRecord,
31
- isString,
32
- jsOnParse,
33
- monokai,
34
- nanolightTs,
35
- newEscape,
36
- newNounForm,
37
- newTokenizer,
38
- omit,
39
- pick,
40
- rwd,
41
- s,
42
- svgUse,
43
- uuidV1,
44
- vivify,
45
-
22
+ csvParse,
23
+ fixPlTypography,
24
+ h,
25
+ hasOwn,
26
+ isArray,
27
+ isFiniteNumber,
28
+ isInteger,
29
+ isNumber,
30
+ isRecord,
31
+ isString,
32
+ jsOnParse,
33
+ monokai,
34
+ nanolightTs,
35
+ newEscape,
36
+ newNounForm,
37
+ newTokenizer,
38
+ omit,
39
+ pick,
40
+ rwd,
41
+ s,
42
+ svgUse,
43
+ uuidV1,
44
+ vivify,
46
45
  } from '@jackens/nnn' // or './node_modules/@jackens/nnn/nnn.js'
47
46
  ```
48
47
 
@@ -216,14 +215,16 @@ A CSS string representing the compiled rules.
216
215
  #### Usage Examples
217
216
 
218
217
  ```ts
219
- const actual1 = c({
220
- a: {
221
- color: 'red',
222
- margin: 1,
223
- '.c': { margin: 2, padding: 2 },
224
- padding: 1,
218
+ const actual1 = c(
219
+ {
220
+ a: {
221
+ color: 'red',
222
+ margin: 1,
223
+ '.c': { margin: 2, padding: 2 },
224
+ padding: 1,
225
+ },
225
226
  },
226
- })
227
+ )
227
228
 
228
229
  const expected1 = `
229
230
  a{
@@ -240,16 +241,18 @@ a{
240
241
 
241
242
  expect(actual1).to.equal(expected1)
242
243
 
243
- const actual2 = c({
244
- a: {
245
- '.b': {
246
- color: 'red',
247
- margin: 1,
248
- '.c': { margin: 2, padding: 2 },
249
- padding: 1,
244
+ const actual2 = c(
245
+ {
246
+ a: {
247
+ '.b': {
248
+ color: 'red',
249
+ margin: 1,
250
+ '.c': { margin: 2, padding: 2 },
251
+ padding: 1,
252
+ },
250
253
  },
251
254
  },
252
- })
255
+ )
253
256
 
254
257
  const expected2 = `
255
258
  a.b{
@@ -266,34 +269,36 @@ a.b{
266
269
 
267
270
  expect(actual2).to.equal(expected2)
268
271
 
269
- const actual3 = c({
270
- '@font-face$$1': {
271
- fontFamily: 'Jackens',
272
- src$$1: 'url(otf/jackens.otf)',
273
- src$$2: "url(otf/jackens.otf) format('opentype')," +
272
+ const actual3 = c(
273
+ {
274
+ '@font-face$$1': {
275
+ fontFamily: 'Jackens',
276
+ src$$1: 'url(otf/jackens.otf)',
277
+ src$$2: "url(otf/jackens.otf) format('opentype')," +
274
278
  "url(svg/jackens.svg) format('svg')",
275
- font_weight: 'normal',
276
- 'font-style': 'normal',
277
- },
278
- '@font-face$$2': {
279
- font_family: 'C64',
280
- src: 'url(fonts/C64_Pro_Mono-STYLE.woff)',
281
- },
282
- '@keyframes spin': {
283
- '0%': { transform: 'rotate(0deg)' },
284
- '100%': { transform: 'rotate(360deg)' },
285
- },
286
- div: {
287
- border: 'solid red 1px',
288
- '.c1': { 'background-color': '#000' },
289
- ' .c1': { background_color: 'black' },
290
- '.c2': { backgroundColor: 'rgb(0,0,0)' },
291
- },
292
- '@media(min-width:200px)': {
293
- div: { margin: 0, padding: 0 },
294
- span: { color: '#000' },
279
+ font_weight: 'normal',
280
+ 'font-style': 'normal',
281
+ },
282
+ '@font-face$$2': {
283
+ font_family: 'C64',
284
+ src: 'url(fonts/C64_Pro_Mono-STYLE.woff)',
285
+ },
286
+ '@keyframes spin': {
287
+ '0%': { transform: 'rotate(0deg)' },
288
+ '100%': { transform: 'rotate(360deg)' },
289
+ },
290
+ div: {
291
+ border: 'solid red 1px',
292
+ '.c1': { 'background-color': '#000' },
293
+ ' .c1': { background_color: 'black' },
294
+ '.c2': { backgroundColor: 'rgb(0,0,0)' },
295
+ },
296
+ '@media(min-width:200px)': {
297
+ div: { margin: 0, padding: 0 },
298
+ span: { color: '#000' },
299
+ },
295
300
  },
296
- })
301
+ )
297
302
 
298
303
  const expected3 = `
299
304
  @font-face{
@@ -339,16 +344,18 @@ div.c2{
339
344
 
340
345
  expect(actual3).to.equal(expected3)
341
346
 
342
- const actual4 = c({
343
- a: {
344
- '.b,.c': {
345
- margin: 1,
346
- '.d': {
347
- margin: 2,
347
+ const actual4 = c(
348
+ {
349
+ a: {
350
+ '.b,.c': {
351
+ margin: 1,
352
+ '.d': {
353
+ margin: 2,
354
+ },
348
355
  },
349
356
  },
350
357
  },
351
- })
358
+ )
352
359
 
353
360
  const expected4 = `
354
361
  a.b,a.c{
@@ -360,14 +367,16 @@ a.b.d,a.c.d{
360
367
 
361
368
  expect(actual4).to.equal(expected4)
362
369
 
363
- const actual5 = c({
364
- '.b,.c': {
365
- margin: 1,
366
- '.d': {
367
- margin: 2,
370
+ const actual5 = c(
371
+ {
372
+ '.b,.c': {
373
+ margin: 1,
374
+ '.d': {
375
+ margin: 2,
376
+ },
368
377
  },
369
378
  },
370
- })
379
+ )
371
380
 
372
381
  const expected5 = `
373
382
  .b,.c{
@@ -379,14 +388,16 @@ const expected5 = `
379
388
 
380
389
  expect(actual5).to.equal(expected5)
381
390
 
382
- const actual6 = c({
383
- '.a,.b': {
384
- margin: 1,
385
- '.c,.d': {
386
- margin: 2,
391
+ const actual6 = c(
392
+ {
393
+ '.a,.b': {
394
+ margin: 1,
395
+ '.c,.d': {
396
+ margin: 2,
397
+ },
387
398
  },
388
399
  },
389
- })
400
+ )
390
401
 
391
402
  const expected6 = `
392
403
  .a,.b{
@@ -436,11 +447,13 @@ yyy",zzz
436
447
 
437
448
  `
438
449
 
439
- expect(csvParse(text)).to.deep.equal([
440
- ['aaa\n"aaa"\naaa', 'bbb', 'ccc,ccc'],
441
- ['xxx,xxx', 'yyy\nyyy', 'zzz'],
442
- [' 42 ', '42', ' 17'],
443
- ])
450
+ expect(csvParse(text)).to.deep.equal(
451
+ [
452
+ ['aaa\n"aaa"\naaa', 'bbb', 'ccc,ccc'],
453
+ ['xxx,xxx', 'yyy\nyyy', 'zzz'],
454
+ [' 42 ', '42', ' 17'],
455
+ ],
456
+ )
444
457
  ```
445
458
 
446
459
  ### fixPlTypography
@@ -922,24 +935,26 @@ const actual1 = jsOnParse(handlers, `[
922
935
  }
923
936
  ]`)
924
937
 
925
- expect(actual1).to.deep.equal([
926
- 3,
927
- 'Hello World!',
928
- {
929
- nested: 'Hello nested World!',
930
- one: 1,
931
- two: 2,
932
- },
933
- 'bar',
934
- {
935
- $foo: [
936
- 'The parent object does not have ' +
937
- 'exactly one property!',
938
- ],
939
- one: 1,
940
- two: 2,
941
- },
942
- ])
938
+ expect(actual1).to.deep.equal(
939
+ [
940
+ 3,
941
+ 'Hello World!',
942
+ {
943
+ nested: 'Hello nested World!',
944
+ one: 1,
945
+ two: 2,
946
+ },
947
+ 'bar',
948
+ {
949
+ $foo: [
950
+ 'The parent object does not have ' +
951
+ 'exactly one property!',
952
+ ],
953
+ one: 1,
954
+ two: 2,
955
+ },
956
+ ],
957
+ )
943
958
 
944
959
  const actual2 = jsOnParse(
945
960
  { $notFunc: 'handler not being a function' } as any,
@@ -1000,30 +1015,32 @@ const codeJs =
1000
1015
  'const answerToLifeTheUniverseAndEverything = ' +
1001
1016
  "{ 42: 42 }['42'] /* 42 */"
1002
1017
 
1003
- expect(nanolightTs(codeJs)).to.deep.equal([
1004
- ['span', { class: 'keyword-1' }, 'const'],
1005
- ' ',
1006
- ['span',
1007
- { class: 'identifier-4' },
1008
- 'answerToLifeTheUniverseAndEverything',
1018
+ expect(nanolightTs(codeJs)).to.deep.equal(
1019
+ [
1020
+ ['span', { class: 'keyword-1' }, 'const'],
1021
+ ' ',
1022
+ ['span',
1023
+ { class: 'identifier-4' },
1024
+ 'answerToLifeTheUniverseAndEverything',
1025
+ ],
1026
+ ' ',
1027
+ ['span', { class: 'operator' }, '='],
1028
+ ' ',
1029
+ ['span', { class: 'punctuation' }, '{'],
1030
+ ' ',
1031
+ ['span', { class: 'number' }, '42'],
1032
+ ['span', { class: 'operator' }, ':'],
1033
+ ' ',
1034
+ ['span', { class: 'number' }, '42'],
1035
+ ' ',
1036
+ ['span', { class: 'punctuation' }, '}'],
1037
+ ['span', { class: 'punctuation' }, '['],
1038
+ ['span', { class: 'string' }, "'42'"],
1039
+ ['span', { class: 'punctuation' }, ']'],
1040
+ ' ',
1041
+ ['span', { class: 'comment' }, '/* 42 */'],
1009
1042
  ],
1010
- ' ',
1011
- ['span', { class: 'operator' }, '='],
1012
- ' ',
1013
- ['span', { class: 'punctuation' }, '{'],
1014
- ' ',
1015
- ['span', { class: 'number' }, '42'],
1016
- ['span', { class: 'operator' }, ':'],
1017
- ' ',
1018
- ['span', { class: 'number' }, '42'],
1019
- ' ',
1020
- ['span', { class: 'punctuation' }, '}'],
1021
- ['span', { class: 'punctuation' }, '['],
1022
- ['span', { class: 'string' }, "'42'"],
1023
- ['span', { class: 'punctuation' }, ']'],
1024
- ' ',
1025
- ['span', { class: 'comment' }, '/* 42 */'],
1026
- ])
1043
+ )
1027
1044
  ```
1028
1045
 
1029
1046
  ### newEscape
@@ -1066,23 +1083,27 @@ const sql = newEscape(escapeFn)
1066
1083
  const actual = sql`
1067
1084
  SELECT *
1068
1085
  FROM table_name
1069
- WHERE column_name IN (${[
1070
- true,
1071
- null,
1072
- undefined,
1073
- NaN,
1074
- Infinity,
1075
- 42,
1076
- '42',
1077
- "4'2",
1078
- /42/,
1079
- new Date(323325000000),
1080
- ]})`
1086
+ WHERE column_name IN (
1087
+ ${[
1088
+ true,
1089
+ null,
1090
+ undefined,
1091
+ NaN,
1092
+ Infinity,
1093
+ 42,
1094
+ '42',
1095
+ "4'2",
1096
+ /42/,
1097
+ new Date(323325000000),
1098
+ ]}
1099
+ )`
1081
1100
 
1082
1101
  const expected = `
1083
1102
  SELECT *
1084
1103
  FROM table_name
1085
- WHERE column_name IN (b'1', NULL, NULL, NULL, NULL, 42, '42', '4''2', NULL, '1980-03-31 04:30:00')`
1104
+ WHERE column_name IN (
1105
+ b'1', NULL, NULL, NULL, NULL, 42, '42', '4''2', NULL, '1980-03-31 04:30:00'
1106
+ )`
1086
1107
 
1087
1108
  expect(actual).to.equal(expected)
1088
1109
  ```
@@ -1206,15 +1227,17 @@ const tokenizer1 = newTokenizer(
1206
1227
  )
1207
1228
  const result1 = tokenizer1('if "hello" else "world"')
1208
1229
 
1209
- expect(result1).to.deep.equal([
1210
- { chunk: 'if', metadata: 'keyword' },
1211
- { chunk: ' ', metadata: undefined },
1212
- { chunk: '"hello"', metadata: 'string' },
1213
- { chunk: ' ', metadata: undefined },
1214
- { chunk: 'else', metadata: 'keyword' },
1215
- { chunk: ' ', metadata: undefined },
1216
- { chunk: '"world"', metadata: 'string' },
1217
- ])
1230
+ expect(result1).to.deep.equal(
1231
+ [
1232
+ { chunk: 'if', metadata: 'keyword' },
1233
+ { chunk: ' ', metadata: undefined },
1234
+ { chunk: '"hello"', metadata: 'string' },
1235
+ { chunk: ' ', metadata: undefined },
1236
+ { chunk: 'else', metadata: 'keyword' },
1237
+ { chunk: ' ', metadata: undefined },
1238
+ { chunk: '"world"', metadata: 'string' },
1239
+ ],
1240
+ )
1218
1241
 
1219
1242
  const tokenizer2 = newTokenizer(
1220
1243
  (chunk, metadata) => `${metadata}:${chunk}`,
@@ -1223,13 +1246,15 @@ const tokenizer2 = newTokenizer(
1223
1246
  )
1224
1247
  const result2 = tokenizer2('aBEGINbENDc')
1225
1248
 
1226
- expect(result2).to.deep.equal([
1227
- 'undefined:a',
1228
- 'tag:BEGIN',
1229
- 'undefined:b',
1230
- 'end:END',
1231
- 'undefined:c',
1232
- ])
1249
+ expect(result2).to.deep.equal(
1250
+ [
1251
+ 'undefined:a',
1252
+ 'tag:BEGIN',
1253
+ 'undefined:b',
1254
+ 'end:END',
1255
+ 'undefined:c',
1256
+ ],
1257
+ )
1233
1258
 
1234
1259
  const tokenizer3 = newTokenizer(
1235
1260
  (chunk) => chunk,
@@ -1245,10 +1270,12 @@ const tokenizer4 = newTokenizer(
1245
1270
  )
1246
1271
  const result4 = tokenizer4('test here')
1247
1272
 
1248
- expect(result4).to.deep.equal([
1249
- { chunk: 'test', metadata: 'start' },
1250
- { chunk: ' here', metadata: undefined },
1251
- ])
1273
+ expect(result4).to.deep.equal(
1274
+ [
1275
+ { chunk: 'test', metadata: 'start' },
1276
+ { chunk: ' here', metadata: undefined },
1277
+ ],
1278
+ )
1252
1279
 
1253
1280
  const tokenizer5 = newTokenizer(
1254
1281
  (chunk, metadata) => metadata,
@@ -1722,48 +1749,58 @@ const ref: any = {}
1722
1749
 
1723
1750
  vivify(ref).one.two[3][4]
1724
1751
 
1725
- expect(ref).to.deep.equal({
1726
- one: {
1727
- two: [undefined, undefined, undefined, []],
1752
+ expect(ref).to.deep.equal(
1753
+ {
1754
+ one: {
1755
+ two: [undefined, undefined, undefined, []],
1756
+ },
1728
1757
  },
1729
- })
1758
+ )
1730
1759
 
1731
1760
  vivify(ref).one.two[3][4] = 5
1732
1761
 
1733
- expect(ref).to.deep.equal({
1734
- one: {
1735
- two: [
1736
- undefined,
1737
- undefined,
1738
- undefined,
1739
- [undefined, undefined, undefined, undefined, 5],
1740
- ],
1762
+ expect(ref).to.deep.equal(
1763
+ {
1764
+ one: {
1765
+ two: [
1766
+ undefined,
1767
+ undefined,
1768
+ undefined,
1769
+ [undefined, undefined, undefined, undefined, 5],
1770
+ ],
1771
+ },
1741
1772
  },
1742
- })
1773
+ )
1743
1774
 
1744
1775
  vivify(ref).one.two[3].length = 1
1745
1776
 
1746
- expect(ref).to.deep.equal({
1747
- one: {
1748
- two: [undefined, undefined, undefined, [undefined]],
1777
+ expect(ref).to.deep.equal(
1778
+ {
1779
+ one: {
1780
+ two: [undefined, undefined, undefined, [undefined]],
1781
+ },
1749
1782
  },
1750
- })
1783
+ )
1751
1784
 
1752
1785
  vivify(ref).one.two[3] = 4
1753
1786
 
1754
- expect(ref).to.deep.equal({
1755
- one: {
1756
- two: [undefined, undefined, undefined, 4],
1787
+ expect(ref).to.deep.equal(
1788
+ {
1789
+ one: {
1790
+ two: [undefined, undefined, undefined, 4],
1791
+ },
1757
1792
  },
1758
- })
1793
+ )
1759
1794
 
1760
1795
  expect(vivify(ref).one.two.length).to.equal(4)
1761
1796
 
1762
- expect(ref).to.deep.equal({
1763
- one: {
1764
- two: [undefined, undefined, undefined, 4],
1797
+ expect(ref).to.deep.equal(
1798
+ {
1799
+ one: {
1800
+ two: [undefined, undefined, undefined, 4],
1801
+ },
1765
1802
  },
1766
- })
1803
+ )
1767
1804
 
1768
1805
  vivify(ref).one.two = 3
1769
1806