@jackens/nnn 2023.8.25 → 2023.8.27-18

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 (3) hide show
  1. package/nnn.d.ts +46 -22
  2. package/nnn.js +135 -86
  3. package/package.json +8 -1
package/nnn.d.ts CHANGED
@@ -1,15 +1,3 @@
1
- /**
2
- * The type of arguments of the `css` helper.
3
- */
4
- export type CNode = {
5
- [attributeOrSelector: string]: string | number | CNode;
6
- };
7
-
8
- /**
9
- * The type of arguments of the `css` helper.
10
- */
11
- export type CRoot = Record<string, CNode>;
12
-
13
1
  /**
14
2
  * The type of arguments of the `escapeValues` and `escape` helpers.
15
3
  */
@@ -24,17 +12,16 @@ export type HArgs = [
24
12
  ];
25
13
 
26
14
  /**
27
- * A simple CSS-in-JS helper.
28
- *
29
- * The `root` parameter provides a hierarchical description of CSS rules.
30
- *
31
- * - Keys of sub-objects whose values are NOT objects are treated as CSS attribute, and values are treated as values of those CSS attributes; the concatenation of keys of all parent objects is a CSS rule.
32
- * - All keys ignore the part starting with a splitter (default: `$$`) sign until the end of the key (e.g. `src$$1` → `src`, `@font-face$$1` → `@font-face`).
33
- * - In keys specifying CSS attribute, all uppercase letters are replaced by lowercase letters with an additional `-` character preceding them (e.g. `fontFamily` → `font-family`).
34
- * - Commas in keys that makes a CSS rule cause it to “split” and create separate rules for each part (e.g. `{div:{margin:1,'.a,.b,.c':{margin:2}}}` → `div{margin:1}div.a,div.b,div.c{margin:2}`).
35
- * - Top-level keys that begin with `@` are not concatenated with sub-object keys.
15
+ * The type of arguments of the `jcss` helper.
36
16
  */
37
- export function css(root: CRoot, splitter?: string): string;
17
+ export type JcssNode = {
18
+ [attributeOrSelector: string]: string | number | JcssNode;
19
+ };
20
+
21
+ /**
22
+ * The type of arguments of the `jcss` helper.
23
+ */
24
+ export type JcssRoot = Record<string, JcssNode>;
38
25
 
39
26
  /**
40
27
  * A helper for creating a chart based on a table (conf. <https://jackens.github.io/nnn/chartable/>).
@@ -76,6 +63,13 @@ export function chartable(options?: {
76
63
  zxY?: number[][];
77
64
  }): SVGSVGElement;
78
65
 
66
+ /**
67
+ * A helper that returns information about the JavaScript engine.
68
+ *
69
+ * It returns an array of triples: `[«name», «prototype-name», «array-of-own-property-names»]`.
70
+ */
71
+ export function engineInfo(): [string, string, string[]][];
72
+
79
73
  /**
80
74
  * A helper that checks equality of the given arguments.
81
75
  */
@@ -136,6 +130,36 @@ export function is(type: SymbolConstructor, arg: any): arg is symbol;
136
130
  export function is(type: undefined, arg: any): arg is null | undefined;
137
131
  export function is<T extends abstract new (...args: any[]) => any>(type: T, arg: any): arg is InstanceType<T>;
138
132
 
133
+ /**
134
+ * A simple CSS-in-JS helper.
135
+ *
136
+ * The `root` parameter provides a hierarchical description of CSS rules.
137
+ *
138
+ * - Keys of sub-objects whose values are NOT objects are treated as CSS attribute, and values are treated as values of those CSS attributes; the concatenation of keys of all parent objects is a CSS rule.
139
+ * - All keys ignore the part starting with a splitter (default: `$$`) sign until the end of the key (e.g. `src$$1` → `src`, `@font-face$$1` → `@font-face`).
140
+ * - In keys specifying CSS attribute, all uppercase letters are replaced by lowercase letters with an additional `-` character preceding them (e.g. `fontFamily` → `font-family`).
141
+ * - Commas in keys that makes a CSS rule cause it to “split” and create separate rules for each part (e.g. `{div:{margin:1,'.a,.b,.c':{margin:2}}}` → `div{margin:1}div.a,div.b,div.c{margin:2}`).
142
+ * - Top-level keys that begin with `@` are not concatenated with sub-object keys.
143
+ */
144
+ export function jcss(root: JcssRoot, splitter?: string): string;
145
+
146
+ /**
147
+ * `JSON.parse` with “JavaScript turned on”.
148
+ *
149
+ * Objects having *exactly* one property which is present in the `handlers` map, i.e. objects of the form:
150
+ *
151
+ * ```js
152
+ * { "«handlerName»": [«params»] }
153
+ * ```
154
+ *
155
+ * are replaced by the result of call
156
+ *
157
+ * ```js
158
+ * handlers['«handlerName»'](...«params»)
159
+ * ```
160
+ */
161
+ export function jsOnParse(handlers: Record<string, Function>, text: string): any;
162
+
139
163
  /**
140
164
  * Language translations helper.
141
165
  */
package/nnn.js CHANGED
@@ -1,3 +1,7 @@
1
+ /**
2
+ * @typedef {Map<any, (value?: any) => string>} EscapeMap
3
+ */
4
+
1
5
  /**
2
6
  * @typedef {[
3
7
  * string | Node,
@@ -7,91 +11,14 @@
7
11
 
8
12
  /**
9
13
  * @typedef {{
10
- * [attributeOrSelector: string]: string | number | CNode;
11
- * }} CNode
12
- */
13
-
14
- /**
15
- * @typedef {Record<string, CNode>} CRoot
14
+ * [attributeOrSelector: string]: string | number | JcssNode;
15
+ * }} JcssNode
16
16
  */
17
17
 
18
18
  /**
19
- * @typedef {Map<any, (value?: any) => string>} EscapeMap
19
+ * @typedef {Record<string, JcssNode>} JcssRoot
20
20
  */
21
21
 
22
- const _css = (
23
- /** @type {CNode} */ node,
24
- /** @type {string} */ prefix,
25
- /** @type {string[]} */ result,
26
- /** @type {(text: string) => string} */ split
27
- ) => {
28
- const /** @type {[CNode | string[], string][]} */ queue = [[node, prefix]]
29
-
30
- while (queue.length) {
31
- const [style2, prefix2] = queue.shift() ?? []
32
-
33
- if (style2 == null || prefix2 == null) {
34
- continue
35
- }
36
-
37
- if (is(Array, style2)) {
38
- result.push(prefix2, prefix2 !== '' ? '{' : '', style2.join(';'), prefix2 !== '' ? '}' : '')
39
- } else {
40
- const /** @type {[CNode | string[], string][]} */ todo = []
41
- let /** @type {string[]} */ attributes = []
42
- let attributesPushed = false
43
-
44
- for (const key in style2) {
45
- const value = style2[key]
46
-
47
- if (is(String, value) || is(Number, value)) {
48
- if (!attributesPushed) {
49
- attributesPushed = true
50
- attributes = []
51
- todo.push([attributes, prefix2])
52
- }
53
-
54
- attributes.push(`${split(key).replace(/([A-Z])/g, (_, letter) => '-' + letter.toLowerCase())}:${value}`)
55
- } else {
56
- attributesPushed = false
57
-
58
- const /** @type {string[]} */ newPrefix = []
59
- const keySplitted = key.split(',')
60
-
61
- for (const prefixItem of prefix2.split(',')) {
62
- for (const keyItem of keySplitted) {
63
- newPrefix.push(prefixItem + keyItem)
64
- }
65
- }
66
-
67
- todo.push([value, newPrefix.join(',')])
68
- }
69
- }
70
-
71
- queue.unshift(...todo)
72
- }
73
- }
74
- }
75
-
76
- export const css = (/** @type {CRoot} */ root, splitter = '$$') => {
77
- const split = (/** @type {string} */ text) => text.split(splitter)[0]
78
- const /** @type {string[]} */ result = []
79
-
80
- for (const key in root) {
81
- const value = root[key]
82
-
83
- if (key[0] === '@') {
84
- result.push(split(key) + '{')
85
- _css(value, '', result, split)
86
- result.push('}')
87
- } else {
88
- _css(value, split(key), result, split)
89
- }
90
- }
91
-
92
- return result.join('')
93
- }
94
-
95
22
  const _COLORS = ['#e22', '#e73', '#fc3', '#ad4', '#4d9', '#3be', '#45d', '#c3e']
96
23
 
97
24
  /**
@@ -284,6 +211,33 @@ export const chartable = ({
284
211
  )
285
212
  }
286
213
 
214
+ export const engineInfo = () => {
215
+ const /** @type {Set<Function>} */ fns = new Set()
216
+ let ref = window
217
+
218
+ while (ref != null) {
219
+ for (const name of Object.getOwnPropertyNames(ref)) {
220
+ // @ts-expect-error
221
+ let fn = ref[name]
222
+
223
+ if (is(Function, fn) && fn.toString?.()?.includes?.('[native code]')) {
224
+ while (fn != null && !fns.has(fn)) {
225
+ fns.add(fn)
226
+ fn = Object.getPrototypeOf(fn)
227
+ }
228
+ }
229
+ }
230
+
231
+ ref = Object.getPrototypeOf(ref)
232
+ }
233
+
234
+ return Array.from(fns.values()).map(/** @returns {[string, string, string[]]} */ fn => [
235
+ fn.name,
236
+ Object.getPrototypeOf(fn)?.name ?? '',
237
+ Object.getOwnPropertyNames(fn.prototype ?? Object.create(null)).sort()
238
+ ]).sort((a, b) => -(a[0] < b[0]))
239
+ }
240
+
287
241
  export const eq = /** @returns {boolean} */ (/** @type {any} */ x, /** @type {any} */ y) => {
288
242
  if (x === y) {
289
243
  return true
@@ -333,11 +287,11 @@ export const escape = (
333
287
  ) => String.raw(template, ...escapeValues(escapeMap, values))
334
288
 
335
289
  export const escapeValues = (
336
- /** @type {EscapeMap} */ escapeMap,
337
- /** @type {any[]} */ values
290
+ /** @type {EscapeMap} */ escapeMap,
291
+ /** @type {any[]} */ values
338
292
  ) => values.map(value => (escapeMap.get(value?.constructor) ?? escapeMap.get(undefined))?.(value) ?? '')
339
293
 
340
- const TAGS_TO_SKIP = { IFRAME: 1, NOSCRIPT: 1, PRE: 1, SCRIPT: 1, STYLE: 1, TEXTAREA: 1 }
294
+ const _TAGS_TO_SKIP = { IFRAME: 1, NOSCRIPT: 1, PRE: 1, SCRIPT: 1, STYLE: 1, TEXTAREA: 1 }
341
295
 
342
296
  export const fixTypography = (/** @type {Node} */ node) => {
343
297
  const /** @type {Node[]} */ queue = [node]
@@ -351,7 +305,7 @@ export const fixTypography = (/** @type {Node} */ node) => {
351
305
 
352
306
  if (childNode instanceof Text) {
353
307
  queue.push(childNode)
354
- } else if (childNode instanceof Element && !has(childNode.tagName, TAGS_TO_SKIP)) {
308
+ } else if (childNode instanceof Element && !has(childNode.tagName, _TAGS_TO_SKIP)) {
355
309
  queue.push(childNode)
356
310
  }
357
311
  }
@@ -395,7 +349,7 @@ export const get = (
395
349
  return ref
396
350
  }
397
351
 
398
- const /** @type {Record<string, string>} */ NS = {
352
+ const /** @type {Record<string, string>} */ _NS = {
399
353
  xlink: 'http://www.w3.org/1999/xlink'
400
354
  }
401
355
 
@@ -452,7 +406,7 @@ const _h = (/** @type {string?=} */ namespaceURI) => {
452
406
  const indexOfColon = name.indexOf(':')
453
407
 
454
408
  if (indexOfColon >= 0) {
455
- const /** @type {string=} */ ns = NS[name.slice(0, indexOfColon)]
409
+ const /** @type {string=} */ ns = _NS[name.slice(0, indexOfColon)]
456
410
 
457
411
  if (ns != null) {
458
412
  const basename = name.slice(indexOfColon + 1)
@@ -511,6 +465,101 @@ export const has = (/** @type {any} */ key, /** @type {any} */ ref) =>
511
465
  */
512
466
  export const is = (/** @type {T} */ type, /** @type {any} */ arg) => arg?.constructor === type
513
467
 
468
+ const _jcss = (
469
+ /** @type {JcssNode} */ node,
470
+ /** @type {string} */ prefix,
471
+ /** @type {string[]} */ result,
472
+ /** @type {(text: string) => string} */ split
473
+ ) => {
474
+ const /** @type {[JcssNode | string[], string][]} */ queue = [[node, prefix]]
475
+
476
+ while (queue.length) {
477
+ const [style2, prefix2] = queue.shift() ?? []
478
+
479
+ if (style2 == null || prefix2 == null) {
480
+ continue
481
+ }
482
+
483
+ if (is(Array, style2)) {
484
+ result.push(prefix2, prefix2 !== '' ? '{' : '', style2.join(';'), prefix2 !== '' ? '}' : '')
485
+ } else {
486
+ const /** @type {[JcssNode | string[], string][]} */ todo = []
487
+ let /** @type {string[]} */ attributes = []
488
+ let attributesPushed = false
489
+
490
+ for (const key in style2) {
491
+ const value = style2[key]
492
+
493
+ if (is(String, value) || is(Number, value)) {
494
+ if (!attributesPushed) {
495
+ attributesPushed = true
496
+ attributes = []
497
+ todo.push([attributes, prefix2])
498
+ }
499
+
500
+ attributes.push(`${split(key).replace(/([A-Z])/g, (_, letter) => '-' + letter.toLowerCase())}:${value}`)
501
+ } else {
502
+ attributesPushed = false
503
+
504
+ const /** @type {string[]} */ newPrefix = []
505
+ const keySplitted = key.split(',')
506
+
507
+ for (const prefixItem of prefix2.split(',')) {
508
+ for (const keyItem of keySplitted) {
509
+ newPrefix.push(prefixItem + keyItem)
510
+ }
511
+ }
512
+
513
+ todo.push([value, newPrefix.join(',')])
514
+ }
515
+ }
516
+
517
+ queue.unshift(...todo)
518
+ }
519
+ }
520
+ }
521
+
522
+ export const jcss = (/** @type {JcssRoot} */ root, splitter = '$$') => {
523
+ const split = (/** @type {string} */ text) => text.split(splitter)[0]
524
+ const /** @type {string[]} */ result = []
525
+
526
+ for (const key in root) {
527
+ const value = root[key]
528
+
529
+ if (key[0] === '@') {
530
+ result.push(split(key) + '{')
531
+ _jcss(value, '', result, split)
532
+ result.push('}')
533
+ } else {
534
+ _jcss(value, split(key), result, split)
535
+ }
536
+ }
537
+
538
+ return result.join('')
539
+ }
540
+
541
+ export const jsOnParse = (
542
+ /** @type {Record<string, Function>} */ handlers,
543
+ /** @type {string} */ text
544
+ ) => JSON.parse(text, (key, value) => {
545
+ if (is(Object, value)) {
546
+ let isSecondKey = false
547
+
548
+ for (key in value) {
549
+ if (isSecondKey) {
550
+ return value
551
+ }
552
+ isSecondKey = true
553
+ }
554
+
555
+ if (has(key, handlers) && is(Array, value[key])) {
556
+ return handlers[key](...value[key])
557
+ }
558
+ }
559
+
560
+ return value
561
+ })
562
+
514
563
  const _locale = (
515
564
  /** @type {Record<string, Record<string, string | Record<string, string>>>} */ locales,
516
565
  /** @type {string} */ language,
package/package.json CHANGED
@@ -9,19 +9,26 @@
9
9
  "CSS-in-JS",
10
10
  "deepEqual",
11
11
  "DOM",
12
+ "eq",
12
13
  "equal",
13
14
  "escape",
15
+ "Gantt",
14
16
  "graph",
15
17
  "h",
18
+ "has",
16
19
  "highlight",
17
20
  "HyperScript",
18
21
  "in",
19
22
  "is",
23
+ "jcss",
24
+ "JSON",
25
+ "jsOnParse",
20
26
  "locale",
21
27
  "localization",
22
28
  "nanolight",
23
29
  "nnn",
24
30
  "RWD",
31
+ "SVG",
25
32
  "translation",
26
33
  "typography",
27
34
  "uuid",
@@ -33,5 +40,5 @@
33
40
  "types": "nnn.d.ts",
34
41
  "name": "@jackens/nnn",
35
42
  "type": "module",
36
- "version": "2023.8.25"
43
+ "version": "2023.8.27-18"
37
44
  }