@jackens/nnn 2026.8.24 → 2026.9.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/nnn.d.ts +2 -105
- package/nnn.js +105 -110
- package/package.json +1 -4
- package/readme.md +27 -514
package/nnn.d.ts
CHANGED
|
@@ -1,106 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Represents a CSS rule node for the {@link c} helper.
|
|
3
|
-
* Keys are CSS properties or nested selectors.
|
|
4
|
-
*/
|
|
5
|
-
export type CNode = {
|
|
6
|
-
[attributeOrSelector: string]: string | number | CNode | undefined;
|
|
7
|
-
};
|
|
8
|
-
/**
|
|
9
|
-
* Represents the root CSS object for the {@link c} helper.
|
|
10
|
-
* Keys are top-level selectors or at-rules.
|
|
11
|
-
*/
|
|
12
|
-
export type CRoot = Record<PropertyKey, CNode>;
|
|
13
|
-
/**
|
|
14
|
-
* A minimal CSS-in-JS helper
|
|
15
|
-
* that converts a JavaScript object hierarchy
|
|
16
|
-
* into a CSS string.
|
|
17
|
-
*
|
|
18
|
-
* @param root
|
|
19
|
-
*
|
|
20
|
-
* An object describing CSS rules.
|
|
21
|
-
* Keys are selectors or at-rules;
|
|
22
|
-
* values are either CSS property values
|
|
23
|
-
* or nested rule objects.
|
|
24
|
-
*
|
|
25
|
-
* @param splitter
|
|
26
|
-
*
|
|
27
|
-
* A delimiter used to create unique keys (default: `'$$'`).
|
|
28
|
-
* The substring from `splitter` to the end of a key
|
|
29
|
-
* is ignored (e.g., `src$$1` → `src`).
|
|
30
|
-
*
|
|
31
|
-
* @returns
|
|
32
|
-
*
|
|
33
|
-
* A CSS string representing the compiled rules.
|
|
34
|
-
*
|
|
35
|
-
* @remarks
|
|
36
|
-
*
|
|
37
|
-
* - Keys whose values are primitives (`string` | `number`)
|
|
38
|
-
* are treated as CSS properties.
|
|
39
|
-
* - In property keys, uppercase letters
|
|
40
|
-
* become lowercase with a `-` prefix
|
|
41
|
-
* (e.g., `fontFamily` → `font-family`);
|
|
42
|
-
* underscores become hyphens
|
|
43
|
-
* (e.g., `font_family` → `font-family`).
|
|
44
|
-
* - Comma-separated selector keys
|
|
45
|
-
* expand into multiple selectors
|
|
46
|
-
* (e.g., `{ div: { '.a,.b': { margin: 1 } } }` →
|
|
47
|
-
* `div.a,div.b{margin:1}`).
|
|
48
|
-
* - Top-level keys starting with `@` (at-rules)
|
|
49
|
-
* are not concatenated with child selectors.
|
|
50
|
-
*/
|
|
51
|
-
export declare const c: (root: CRoot, splitter?: string) => string;
|
|
52
|
-
/**
|
|
53
|
-
* Creates a tag function
|
|
54
|
-
* for escaping interpolated values in template literals.
|
|
55
|
-
*
|
|
56
|
-
* @param escapeFn
|
|
57
|
-
*
|
|
58
|
-
* A function that takes a value
|
|
59
|
-
* and returns its escaped string representation.
|
|
60
|
-
*
|
|
61
|
-
* @returns
|
|
62
|
-
*
|
|
63
|
-
* A tag function that escapes interpolated values
|
|
64
|
-
* using the provided escape map.
|
|
65
|
-
*/
|
|
66
|
-
export declare const createEscape: (escapeFn: (value: any) => string) => (template: TemplateStringsArray, ...values: unknown[]) => string;
|
|
67
|
-
/**
|
|
68
|
-
* Creates a function that
|
|
69
|
-
* returns the appropriate noun form
|
|
70
|
-
* based on a numeric value using `Intl.PluralRules`.
|
|
71
|
-
*
|
|
72
|
-
* Different languages have different plural rules.
|
|
73
|
-
* The `Intl.PluralRules` API provides locale-aware
|
|
74
|
-
* plural category selection.
|
|
75
|
-
* Possible categories are:
|
|
76
|
-
*
|
|
77
|
-
* - `zero`: for zero items
|
|
78
|
-
* (used in some languages like Arabic, Latvian)
|
|
79
|
-
* - `one`: for singular (e.g., 1 item)
|
|
80
|
-
* - `two`: for dual
|
|
81
|
-
* (used in some languages like Arabic, Hebrew)
|
|
82
|
-
* - `few`: for small plurals (e.g., 2-4 in Polish)
|
|
83
|
-
* - `many`: for larger plurals (e.g., 5-21 in Polish)
|
|
84
|
-
* - `other`: fallback category (used by all languages)
|
|
85
|
-
*
|
|
86
|
-
* @param locale
|
|
87
|
-
*
|
|
88
|
-
* A BCP 47 language tag (e.g., `pl`, `en`).
|
|
89
|
-
*
|
|
90
|
-
* @param forms
|
|
91
|
-
*
|
|
92
|
-
* An object mapping plural categories to noun forms.
|
|
93
|
-
* Not all categories need to be provided;
|
|
94
|
-
* if a category is missing,
|
|
95
|
-
* the function falls back to `other`,
|
|
96
|
-
* then to an empty string.
|
|
97
|
-
*
|
|
98
|
-
* @returns
|
|
99
|
-
*
|
|
100
|
-
* A function that takes a numeric value
|
|
101
|
-
* and returns the appropriate noun form.
|
|
102
|
-
*/
|
|
103
|
-
export declare const createPluralRules: (locale: string, forms: Partial<Record<Intl.LDMLPluralRule, string>>) => (value: number) => string;
|
|
104
1
|
/**
|
|
105
2
|
* A helper for building simple tokenizers
|
|
106
3
|
* (see also {@link nanolightTs}).
|
|
@@ -400,7 +297,7 @@ export declare const jsOnParse: (handlers: Record<PropertyKey, Function>, text:
|
|
|
400
297
|
* for use with the {@link c} helper
|
|
401
298
|
* and {@link nanolightTs} tokenizer.
|
|
402
299
|
*/
|
|
403
|
-
export declare const monokai:
|
|
300
|
+
export declare const monokai: CNode;
|
|
404
301
|
/**
|
|
405
302
|
* A TypeScript/JavaScript syntax highlighting tokenizer
|
|
406
303
|
* built using {@link createTokenizer}.
|
|
@@ -487,7 +384,7 @@ export declare const pick: <T, K extends keyof T>(ref: T, keys: unknown[]) => Pi
|
|
|
487
384
|
* - `height` (optional, default `1`):
|
|
488
385
|
* number of vertical cells the element spans.
|
|
489
386
|
*/
|
|
490
|
-
export declare const rwd: (root:
|
|
387
|
+
export declare const rwd: (root: CNode, selector: string, cellWidthPx: number, cellHeightPx: number, ...specs: [number, number?, number?][]) => void;
|
|
491
388
|
/**
|
|
492
389
|
* Generates a UUID v1 (time-based) identifier.
|
|
493
390
|
*
|
package/nnn.js
CHANGED
|
@@ -6,59 +6,59 @@ var isString = (arg) => typeof arg === "string";
|
|
|
6
6
|
var isRecord = (arg) => typeof arg === "object" && arg != null && !isArray(arg);
|
|
7
7
|
|
|
8
8
|
// src/nnn/c.ts
|
|
9
|
-
var
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
let attributes = [];
|
|
18
|
-
let attributesPushed = false;
|
|
19
|
-
for (const key in style0) {
|
|
20
|
-
const value = style0[key];
|
|
21
|
-
if (isString(value) || isNumber(value)) {
|
|
22
|
-
if (!attributesPushed) {
|
|
23
|
-
attributesPushed = true;
|
|
24
|
-
attributes = [];
|
|
25
|
-
todo.push([attributes, prefix0]);
|
|
26
|
-
}
|
|
27
|
-
const attribute = key.split(splitter)[0].replace(/_/g, "-").replace(/([A-Z])/g, (_, letter) => "-" + letter.toLowerCase());
|
|
28
|
-
attributes.push(`${attribute}:${value}`);
|
|
29
|
-
} else if (value != null) {
|
|
30
|
-
attributesPushed = false;
|
|
31
|
-
const prefixN = [];
|
|
32
|
-
const keyChunks = key.split(",");
|
|
33
|
-
prefix0.split(",").forEach((prefixChunk) => keyChunks.forEach((keyChunk) => prefixN.push(prefixChunk + keyChunk)));
|
|
34
|
-
todo.push([value, prefixN.join()]);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
queue.unshift(...todo);
|
|
9
|
+
var DEFAULT_SPLITTER = "$$";
|
|
10
|
+
var combinePrefixAndName = (prefix, name) => {
|
|
11
|
+
const prefixes = prefix === "" ? [""] : prefix.split(",");
|
|
12
|
+
const names = name.split(",");
|
|
13
|
+
const combined = [];
|
|
14
|
+
for (const p of prefixes) {
|
|
15
|
+
for (const n of names) {
|
|
16
|
+
combined.push(p + n);
|
|
38
17
|
}
|
|
39
18
|
}
|
|
19
|
+
return combined.join(",");
|
|
40
20
|
};
|
|
41
|
-
var
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
if (
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
21
|
+
var _c = (node, splitter = DEFAULT_SPLITTER, prefix = "", flatten = false) => {
|
|
22
|
+
const out = [];
|
|
23
|
+
let buffer = [];
|
|
24
|
+
const flush = () => {
|
|
25
|
+
if (buffer.length === 0) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
out.push(prefix === "" ? buffer.join(";") : `${prefix}{${buffer.join(";")}}`);
|
|
29
|
+
buffer = [];
|
|
30
|
+
};
|
|
31
|
+
for (const [rawKey, value] of Object.entries(node)) {
|
|
32
|
+
if (value == null) {
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
const splitterIndex = rawKey.indexOf(splitter);
|
|
36
|
+
const name = splitterIndex < 0 ? rawKey : rawKey.slice(0, splitterIndex);
|
|
37
|
+
if (isNumber(value) || isString(value)) {
|
|
38
|
+
if (flatten) {
|
|
39
|
+
out.push(`${prefix + name}:${value}`);
|
|
50
40
|
} else {
|
|
51
|
-
|
|
41
|
+
buffer.push(`${name}:${value}`);
|
|
52
42
|
}
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
if (flatten) {
|
|
46
|
+
out.push(_c(value, splitter, prefix + name, true));
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
flush();
|
|
50
|
+
if (prefix === "" && (name === ":root" || name[0] === "@")) {
|
|
51
|
+
out.push(`${name}{${_c(value, splitter, "", name === ":root")}}`);
|
|
52
|
+
} else {
|
|
53
|
+
out.push(_c(value, splitter, combinePrefixAndName(prefix, name), false));
|
|
53
54
|
}
|
|
54
55
|
}
|
|
55
|
-
|
|
56
|
+
flush();
|
|
57
|
+
return out.join(flatten ? ";" : "");
|
|
56
58
|
};
|
|
59
|
+
var c = (node, splitter = DEFAULT_SPLITTER) => _c(node, splitter);
|
|
57
60
|
// src/nnn/createEscape.ts
|
|
58
61
|
var createEscape = (escapeFn) => (template, ...values) => String.raw(template, ...values.map(escapeFn));
|
|
59
|
-
// src/nnn/createPluralRules.ts
|
|
60
|
-
var PLURAL_RULES = {};
|
|
61
|
-
var createPluralRules = (locale, forms) => (value) => forms[(PLURAL_RULES[locale] ??= new Intl.PluralRules(locale)).select(value)] ?? forms.other ?? "";
|
|
62
62
|
// src/nnn/createTokenizer.ts
|
|
63
63
|
var createTokenizer = (decorator, ...specs) => (code) => {
|
|
64
64
|
const result = [];
|
|
@@ -201,51 +201,38 @@ var fixPlTypography = (node) => {
|
|
|
201
201
|
};
|
|
202
202
|
// src/nnn/hasOwn.ts
|
|
203
203
|
var hasOwn = (ref, key) => ref != null && Object.hasOwn(ref, key);
|
|
204
|
-
// src/nnn/jsOnParse.ts
|
|
205
|
-
var jsOnParse = (handlers, text) => JSON.parse(text, (key, value) => {
|
|
206
|
-
if (isRecord(value)) {
|
|
207
|
-
let isSecondKey = false;
|
|
208
|
-
for (key in value) {
|
|
209
|
-
if (isSecondKey) {
|
|
210
|
-
return value;
|
|
211
|
-
}
|
|
212
|
-
isSecondKey = true;
|
|
213
|
-
}
|
|
214
|
-
const handler = handlers[key];
|
|
215
|
-
const params = value[key];
|
|
216
|
-
if (handler instanceof Function && isArray(params)) {
|
|
217
|
-
return handler(...params);
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
return value;
|
|
221
|
-
});
|
|
222
204
|
// src/nnn/monokai.ts
|
|
223
|
-
var darkKey = "@media only screen and (prefers-color-scheme: dark)" + "$$monokai";
|
|
224
205
|
var monokai = {
|
|
225
206
|
":root$$monokai": {
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
207
|
+
"--": {
|
|
208
|
+
bg: "#faf4f2",
|
|
209
|
+
fg: "#29242a",
|
|
210
|
+
comment: "#918c8e",
|
|
211
|
+
identifier: {
|
|
212
|
+
1: "#7058be",
|
|
213
|
+
2: "#269d69",
|
|
214
|
+
3: "#1c8ca8",
|
|
215
|
+
4: "#29242a"
|
|
216
|
+
},
|
|
217
|
+
keyword: {
|
|
218
|
+
1: "#e14775",
|
|
219
|
+
2: "#7058be",
|
|
220
|
+
3: "#1c8ca8"
|
|
221
|
+
},
|
|
222
|
+
number: "#7058be",
|
|
223
|
+
operator: "#e14775",
|
|
224
|
+
punctuation: "#918c8e",
|
|
225
|
+
string: "#cc7a0a"
|
|
226
|
+
}
|
|
240
227
|
},
|
|
241
228
|
pre$$monokai: {
|
|
242
|
-
|
|
229
|
+
"background-color": "var(--bg)",
|
|
243
230
|
color: "var(--fg)",
|
|
244
231
|
margin: 0,
|
|
245
232
|
padding: "1em",
|
|
246
233
|
overflow: "visible",
|
|
247
234
|
" code": {
|
|
248
|
-
|
|
235
|
+
"font-family": '"Source Code Pro"',
|
|
249
236
|
padding: 0
|
|
250
237
|
}
|
|
251
238
|
},
|
|
@@ -253,51 +240,61 @@ var monokai = {
|
|
|
253
240
|
bg: { color: "var(--bg)" },
|
|
254
241
|
fg: { color: "var(--fg)" },
|
|
255
242
|
comment: { color: "var(--comment)" },
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
243
|
+
identifier: {
|
|
244
|
+
1: { color: "var(--identifier1)" },
|
|
245
|
+
2: { color: "var(--identifier2)" },
|
|
246
|
+
3: { color: "var(--identifier3)" },
|
|
247
|
+
4: { color: "var(--identifier4)" }
|
|
248
|
+
},
|
|
249
|
+
keyword: {
|
|
250
|
+
1: { color: "var(--keyword1)" },
|
|
251
|
+
2: { color: "var(--keyword2)" },
|
|
252
|
+
3: { color: "var(--keyword3)" }
|
|
253
|
+
},
|
|
263
254
|
number: { color: "var(--number)" },
|
|
264
255
|
operator: { color: "var(--operator)" },
|
|
265
256
|
punctuation: { color: "var(--punctuation)" },
|
|
266
257
|
string: { color: "var(--string)" }
|
|
267
258
|
},
|
|
268
|
-
|
|
259
|
+
"@media only screen and (prefers-color-scheme: dark)$$monokai": {
|
|
269
260
|
":root": {
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
261
|
+
"--": {
|
|
262
|
+
bg: "#2d2a2e",
|
|
263
|
+
fg: "#fcfcfa",
|
|
264
|
+
comment: "#727072",
|
|
265
|
+
identifier: {
|
|
266
|
+
1: "#ae81ff",
|
|
267
|
+
2: "#a9dc76",
|
|
268
|
+
3: "#66d9ef",
|
|
269
|
+
4: "#fcfcfa"
|
|
270
|
+
},
|
|
271
|
+
keyword: {
|
|
272
|
+
1: "#ff6188",
|
|
273
|
+
2: "#ae81ff",
|
|
274
|
+
3: "#66d9ef"
|
|
275
|
+
},
|
|
276
|
+
number: "#ae81ff",
|
|
277
|
+
operator: "#ff6188",
|
|
278
|
+
punctuation: "#727072",
|
|
279
|
+
string: "#ffd866"
|
|
280
|
+
}
|
|
284
281
|
}
|
|
285
282
|
}
|
|
286
283
|
};
|
|
287
284
|
// src/nnn/nanolightTs.ts
|
|
288
285
|
var COMMENT = "comment";
|
|
289
|
-
var
|
|
290
|
-
var
|
|
291
|
-
var
|
|
292
|
-
var
|
|
293
|
-
var
|
|
294
|
-
var
|
|
295
|
-
var
|
|
286
|
+
var IDENTIFIER1 = "identifier1";
|
|
287
|
+
var IDENTIFIER2 = "identifier2";
|
|
288
|
+
var IDENTIFIER3 = "identifier3";
|
|
289
|
+
var IDENTIFIER4 = "identifier4";
|
|
290
|
+
var KEYWORD1 = "keyword1";
|
|
291
|
+
var KEYWORD2 = "keyword2";
|
|
292
|
+
var KEYWORD3 = "keyword3";
|
|
296
293
|
var NUMBER = "number";
|
|
297
294
|
var OPERATOR = "operator";
|
|
298
295
|
var PUNCTUATION = "punctuation";
|
|
299
296
|
var STRING = "string";
|
|
300
|
-
var nanolightTs = /* @__PURE__ */ createTokenizer((chunk, name) => name != null ? ["span", { class: name }, chunk] : chunk, [COMMENT, /\/\*.*?\*\//s], [COMMENT, /(?<!\\)\/\/.*?(?=\n)/], [STRING, /".*?"/], [STRING, /'.*?'/], [STRING, /`.*?`/s], [STRING, /\/[^\s]*[^\\]\/[dgimsuvy]*/], [NUMBER, /0b[01_]+/], [NUMBER, /0o[01234567_]+/], [NUMBER, /0x[\dabcdef_]+/], [NUMBER, /\d[\d_]*(\.[\d_]+)?(e[+-]?[\d_]+)?/], [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, "~="], [OPERATOR, /(?<=\s):/], [PUNCTUATION, "("], [PUNCTUATION, ")"], [PUNCTUATION, ","], [PUNCTUATION, "."], [PUNCTUATION, ":"], [PUNCTUATION, ";"], [PUNCTUATION, "?."], [PUNCTUATION, "["], [PUNCTUATION, "]"], [PUNCTUATION, "{"], [PUNCTUATION, "}"], [
|
|
297
|
+
var nanolightTs = /* @__PURE__ */ createTokenizer((chunk, name) => name != null ? ["span", { class: name }, chunk] : chunk, [COMMENT, /\/\*.*?\*\//s], [COMMENT, /(?<!\\)\/\/.*?(?=\n)/], [STRING, /".*?"/], [STRING, /'.*?'/], [STRING, /`.*?`/s], [STRING, /\/[^\s]*[^\\]\/[dgimsuvy]*/], [NUMBER, /0b[01_]+/], [NUMBER, /0o[01234567_]+/], [NUMBER, /0x[\dabcdef_]+/], [NUMBER, /\d[\d_]*(\.[\d_]+)?(e[+-]?[\d_]+)?/], [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, "~="], [OPERATOR, /(?<=\s):/], [PUNCTUATION, "("], [PUNCTUATION, ")"], [PUNCTUATION, ","], [PUNCTUATION, "."], [PUNCTUATION, ":"], [PUNCTUATION, ";"], [PUNCTUATION, "?."], [PUNCTUATION, "["], [PUNCTUATION, "]"], [PUNCTUATION, "{"], [PUNCTUATION, "}"], [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"], [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]);
|
|
301
298
|
// src/nnn/omit.ts
|
|
302
299
|
var omit = (ref, keys) => Object.fromEntries(Object.entries(ref).filter(([key]) => !keys.includes(key)));
|
|
303
300
|
// src/nnn/pick.ts
|
|
@@ -358,7 +355,6 @@ var vivify = (ref) => _vivify({ _: ref }, "_");
|
|
|
358
355
|
export {
|
|
359
356
|
c,
|
|
360
357
|
createEscape,
|
|
361
|
-
createPluralRules as createNounForm,
|
|
362
358
|
createTokenizer,
|
|
363
359
|
csvParse,
|
|
364
360
|
fixPlTypography,
|
|
@@ -369,7 +365,6 @@ export {
|
|
|
369
365
|
isNumber,
|
|
370
366
|
isRecord,
|
|
371
367
|
isString,
|
|
372
|
-
jsOnParse,
|
|
373
368
|
monokai,
|
|
374
369
|
nanolightTs,
|
|
375
370
|
omit,
|
package/package.json
CHANGED
|
@@ -20,12 +20,9 @@
|
|
|
20
20
|
"isRecord",
|
|
21
21
|
"isString",
|
|
22
22
|
"JS-to-CSS",
|
|
23
|
-
"jsOnParse",
|
|
24
|
-
"JSON",
|
|
25
23
|
"Monokai",
|
|
26
24
|
"nanolight",
|
|
27
25
|
"nnn",
|
|
28
|
-
"noun form",
|
|
29
26
|
"omit",
|
|
30
27
|
"pick",
|
|
31
28
|
"Responsive Web Design",
|
|
@@ -44,5 +41,5 @@
|
|
|
44
41
|
"name": "@jackens/nnn",
|
|
45
42
|
"type": "module",
|
|
46
43
|
"types": "nnn.d.ts",
|
|
47
|
-
"version": "2026.
|
|
44
|
+
"version": "2026.9.1"
|
|
48
45
|
}
|
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.9.1`).
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -18,9 +18,6 @@ npm i @jackens/nnn
|
|
|
18
18
|
|
|
19
19
|
```js
|
|
20
20
|
import {
|
|
21
|
-
c,
|
|
22
|
-
createEscape,
|
|
23
|
-
createPluralRules,
|
|
24
21
|
createTokenizer,
|
|
25
22
|
csvParse,
|
|
26
23
|
fixPlTypography,
|
|
@@ -47,27 +44,12 @@ import {
|
|
|
47
44
|
|
|
48
45
|
## Exports
|
|
49
46
|
|
|
50
|
-
- [`CNode`](#CNode):
|
|
51
|
-
Represents a CSS rule node for the [`c`](#c) helper.
|
|
52
|
-
- [`CRoot`](#CRoot):
|
|
53
|
-
Represents the root CSS object for the [`c`](#c) helper.
|
|
54
47
|
- [`HArgs`](#HArgs):
|
|
55
48
|
Tuple argument type for the [`h`](#h) and [`s`](#s)
|
|
56
49
|
helpers.
|
|
57
50
|
- [`HArgs1`](#HArgs1):
|
|
58
51
|
Single argument type for the [`h`](#h) and [`s`](#s)
|
|
59
52
|
helpers.
|
|
60
|
-
- [`c`](#c):
|
|
61
|
-
A minimal CSS-in-JS helper
|
|
62
|
-
that converts a JavaScript object hierarchy
|
|
63
|
-
into a CSS string.
|
|
64
|
-
- [`createEscape`](#createEscape):
|
|
65
|
-
Creates a tag function
|
|
66
|
-
for escaping interpolated values in template literals.
|
|
67
|
-
- [`createPluralRules`](#createPluralRules):
|
|
68
|
-
Creates a function that
|
|
69
|
-
returns the appropriate noun form
|
|
70
|
-
based on a numeric value using `Intl.PluralRules`.
|
|
71
53
|
- [`createTokenizer`](#createTokenizer):
|
|
72
54
|
A helper for building simple tokenizers
|
|
73
55
|
(see also [`nanolightTs`](#nanolightTs)).
|
|
@@ -104,7 +86,7 @@ import {
|
|
|
104
86
|
handler-based value transformation (“JavaScript ON”).
|
|
105
87
|
- [`monokai`](#monokai):
|
|
106
88
|
A Monokai-inspired color scheme
|
|
107
|
-
for use with the
|
|
89
|
+
for use with the {@link c} helper
|
|
108
90
|
and [`nanolightTs`](#nanolightTs) tokenizer.
|
|
109
91
|
- [`nanolightTs`](#nanolightTs):
|
|
110
92
|
A TypeScript/JavaScript syntax highlighting tokenizer
|
|
@@ -131,26 +113,6 @@ import {
|
|
|
131
113
|
A Proxy-based helper for auto-vivification
|
|
132
114
|
of nested object structures.
|
|
133
115
|
|
|
134
|
-
### CNode
|
|
135
|
-
|
|
136
|
-
```ts
|
|
137
|
-
type CNode = {
|
|
138
|
-
[attributeOrSelector: string]: string | number | CNode | undefined;
|
|
139
|
-
};
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
Represents a CSS rule node for the [`c`](#c) helper.
|
|
143
|
-
Keys are CSS properties or nested selectors.
|
|
144
|
-
|
|
145
|
-
### CRoot
|
|
146
|
-
|
|
147
|
-
```ts
|
|
148
|
-
type CRoot = Record<PropertyKey, CNode>;
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
Represents the root CSS object for the [`c`](#c) helper.
|
|
152
|
-
Keys are top-level selectors or at-rules.
|
|
153
|
-
|
|
154
116
|
### HArgs
|
|
155
117
|
|
|
156
118
|
```ts
|
|
@@ -169,376 +131,6 @@ type HArgs1 = Record<PropertyKey, unknown> | null | undefined | Node | string |
|
|
|
169
131
|
Single argument type for the [`h`](#h) and [`s`](#s)
|
|
170
132
|
helpers.
|
|
171
133
|
|
|
172
|
-
### c
|
|
173
|
-
|
|
174
|
-
```ts
|
|
175
|
-
const c: (root: CRoot, splitter?: string) => string;
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
A minimal CSS-in-JS helper
|
|
179
|
-
that converts a JavaScript object hierarchy
|
|
180
|
-
into a CSS string.
|
|
181
|
-
|
|
182
|
-
#### root
|
|
183
|
-
|
|
184
|
-
An object describing CSS rules.
|
|
185
|
-
Keys are selectors or at-rules;
|
|
186
|
-
values are either CSS property values
|
|
187
|
-
or nested rule objects.
|
|
188
|
-
|
|
189
|
-
#### splitter
|
|
190
|
-
|
|
191
|
-
A delimiter used to create unique keys (default: `'$$'`).
|
|
192
|
-
The substring from `splitter` to the end of a key
|
|
193
|
-
is ignored (e.g., `src$$1` → `src`).
|
|
194
|
-
|
|
195
|
-
#### Returns
|
|
196
|
-
|
|
197
|
-
A CSS string representing the compiled rules.
|
|
198
|
-
|
|
199
|
-
#### Remarks
|
|
200
|
-
|
|
201
|
-
- Keys whose values are primitives (`string` | `number`)
|
|
202
|
-
are treated as CSS properties.
|
|
203
|
-
- In property keys, uppercase letters
|
|
204
|
-
become lowercase with a `-` prefix
|
|
205
|
-
(e.g., `fontFamily` → `font-family`);
|
|
206
|
-
underscores become hyphens
|
|
207
|
-
(e.g., `font_family` → `font-family`).
|
|
208
|
-
- Comma-separated selector keys
|
|
209
|
-
expand into multiple selectors
|
|
210
|
-
(e.g., `{ div: { '.a,.b': { margin: 1 } } }` →
|
|
211
|
-
`div.a,div.b{margin:1}`).
|
|
212
|
-
- Top-level keys starting with `@` (at-rules)
|
|
213
|
-
are not concatenated with child selectors.
|
|
214
|
-
|
|
215
|
-
#### Usage Examples
|
|
216
|
-
|
|
217
|
-
```ts
|
|
218
|
-
const actual1 = c({
|
|
219
|
-
a: {
|
|
220
|
-
color: 'red',
|
|
221
|
-
margin: 1,
|
|
222
|
-
'.c': { margin: 2, padding: 2 },
|
|
223
|
-
padding: 1,
|
|
224
|
-
},
|
|
225
|
-
})
|
|
226
|
-
|
|
227
|
-
const expected1 = `
|
|
228
|
-
a{
|
|
229
|
-
color:red;
|
|
230
|
-
margin:1
|
|
231
|
-
}
|
|
232
|
-
a.c{
|
|
233
|
-
margin:2;
|
|
234
|
-
padding:2
|
|
235
|
-
}
|
|
236
|
-
a{
|
|
237
|
-
padding:1
|
|
238
|
-
}`.replace(/\n\s*/g, '')
|
|
239
|
-
|
|
240
|
-
expect(actual1).to.equal(expected1)
|
|
241
|
-
|
|
242
|
-
const actual2 = c({
|
|
243
|
-
a: {
|
|
244
|
-
'.b': {
|
|
245
|
-
color: 'red',
|
|
246
|
-
margin: 1,
|
|
247
|
-
'.c': { margin: 2, padding: 2 },
|
|
248
|
-
padding: 1,
|
|
249
|
-
},
|
|
250
|
-
},
|
|
251
|
-
})
|
|
252
|
-
|
|
253
|
-
const expected2 = `
|
|
254
|
-
a.b{
|
|
255
|
-
color:red;
|
|
256
|
-
margin:1
|
|
257
|
-
}
|
|
258
|
-
a.b.c{
|
|
259
|
-
margin:2;
|
|
260
|
-
padding:2
|
|
261
|
-
}
|
|
262
|
-
a.b{
|
|
263
|
-
padding:1
|
|
264
|
-
}`.replace(/\n\s*/g, '')
|
|
265
|
-
|
|
266
|
-
expect(actual2).to.equal(expected2)
|
|
267
|
-
|
|
268
|
-
const actual3 = c({
|
|
269
|
-
'@font-face$$1': {
|
|
270
|
-
fontFamily: 'Jackens',
|
|
271
|
-
src$$1: 'url(otf/jackens.otf)',
|
|
272
|
-
src$$2:
|
|
273
|
-
"url(otf/jackens.otf) format('opentype')," +
|
|
274
|
-
"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' },
|
|
295
|
-
},
|
|
296
|
-
})
|
|
297
|
-
|
|
298
|
-
const expected3 = `
|
|
299
|
-
@font-face{
|
|
300
|
-
font-family:Jackens;
|
|
301
|
-
src:url(otf/jackens.otf);
|
|
302
|
-
src:url(otf/jackens.otf) format('opentype'),url(svg/jackens.svg) format('svg');
|
|
303
|
-
font-weight:normal;
|
|
304
|
-
font-style:normal
|
|
305
|
-
}
|
|
306
|
-
@font-face{
|
|
307
|
-
font-family:C64;
|
|
308
|
-
src:url(fonts/C64_Pro_Mono-STYLE.woff)
|
|
309
|
-
}
|
|
310
|
-
@keyframes spin{
|
|
311
|
-
0%{
|
|
312
|
-
transform:rotate(0deg)
|
|
313
|
-
}
|
|
314
|
-
100%{
|
|
315
|
-
transform:rotate(360deg)
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
div{
|
|
319
|
-
border:solid red 1px
|
|
320
|
-
}
|
|
321
|
-
div.c1{
|
|
322
|
-
background-color:#000
|
|
323
|
-
}
|
|
324
|
-
div .c1{
|
|
325
|
-
background-color:black
|
|
326
|
-
}
|
|
327
|
-
div.c2{
|
|
328
|
-
background-color:rgb(0,0,0)
|
|
329
|
-
}
|
|
330
|
-
@media(min-width:200px){
|
|
331
|
-
div{
|
|
332
|
-
margin:0;
|
|
333
|
-
padding:0
|
|
334
|
-
}
|
|
335
|
-
span{
|
|
336
|
-
color:#000
|
|
337
|
-
}
|
|
338
|
-
}`.replace(/\n\s*/g, '')
|
|
339
|
-
|
|
340
|
-
expect(actual3).to.equal(expected3)
|
|
341
|
-
|
|
342
|
-
const actual4 = c({
|
|
343
|
-
a: {
|
|
344
|
-
'.b,.c': {
|
|
345
|
-
margin: 1,
|
|
346
|
-
'.d': {
|
|
347
|
-
margin: 2,
|
|
348
|
-
},
|
|
349
|
-
},
|
|
350
|
-
},
|
|
351
|
-
})
|
|
352
|
-
|
|
353
|
-
const expected4 = `
|
|
354
|
-
a.b,a.c{
|
|
355
|
-
margin:1
|
|
356
|
-
}
|
|
357
|
-
a.b.d,a.c.d{
|
|
358
|
-
margin:2
|
|
359
|
-
}`.replace(/\n\s*/g, '')
|
|
360
|
-
|
|
361
|
-
expect(actual4).to.equal(expected4)
|
|
362
|
-
|
|
363
|
-
const actual5 = c({
|
|
364
|
-
'.b,.c': {
|
|
365
|
-
margin: 1,
|
|
366
|
-
'.d': {
|
|
367
|
-
margin: 2,
|
|
368
|
-
},
|
|
369
|
-
},
|
|
370
|
-
})
|
|
371
|
-
|
|
372
|
-
const expected5 = `
|
|
373
|
-
.b,.c{
|
|
374
|
-
margin:1
|
|
375
|
-
}
|
|
376
|
-
.b.d,.c.d{
|
|
377
|
-
margin:2
|
|
378
|
-
}`.replace(/\n\s*/g, '')
|
|
379
|
-
|
|
380
|
-
expect(actual5).to.equal(expected5)
|
|
381
|
-
|
|
382
|
-
const actual6 = c({
|
|
383
|
-
'.a,.b': {
|
|
384
|
-
margin: 1,
|
|
385
|
-
'.c,.d': {
|
|
386
|
-
margin: 2,
|
|
387
|
-
},
|
|
388
|
-
},
|
|
389
|
-
})
|
|
390
|
-
|
|
391
|
-
const expected6 = `
|
|
392
|
-
.a,.b{
|
|
393
|
-
margin:1
|
|
394
|
-
}
|
|
395
|
-
.a.c,.a.d,.b.c,.b.d{
|
|
396
|
-
margin:2
|
|
397
|
-
}`.replace(/\n\s*/g, '')
|
|
398
|
-
|
|
399
|
-
expect(actual6).to.equal(expected6)
|
|
400
|
-
```
|
|
401
|
-
|
|
402
|
-
### createEscape
|
|
403
|
-
|
|
404
|
-
```ts
|
|
405
|
-
const createEscape: (escapeFn: (value: any) => string) => (template: TemplateStringsArray, ...values: unknown[]) => string;
|
|
406
|
-
```
|
|
407
|
-
|
|
408
|
-
Creates a tag function
|
|
409
|
-
for escaping interpolated values in template literals.
|
|
410
|
-
|
|
411
|
-
#### escapeFn
|
|
412
|
-
|
|
413
|
-
A function that takes a value
|
|
414
|
-
and returns its escaped string representation.
|
|
415
|
-
|
|
416
|
-
#### Returns
|
|
417
|
-
|
|
418
|
-
A tag function that escapes interpolated values
|
|
419
|
-
using the provided escape map.
|
|
420
|
-
|
|
421
|
-
#### Usage Examples
|
|
422
|
-
|
|
423
|
-
```ts
|
|
424
|
-
const escapeFn = (value: any): string =>
|
|
425
|
-
isArray(value)
|
|
426
|
-
? value.map(escapeFn).join(', ')
|
|
427
|
-
: value === true || value === false
|
|
428
|
-
? `b'${+value}'`
|
|
429
|
-
: value instanceof Date
|
|
430
|
-
? `'${value
|
|
431
|
-
.toISOString()
|
|
432
|
-
.replace(/^(.+)T(.+)\..*$/, '$1 $2')}'`
|
|
433
|
-
: isFiniteNumber(value)
|
|
434
|
-
? `${value}`
|
|
435
|
-
: isString(value)
|
|
436
|
-
? `'${value.replace(/'/g, "''")}'`
|
|
437
|
-
: 'NULL'
|
|
438
|
-
|
|
439
|
-
const sql = createEscape(escapeFn)
|
|
440
|
-
|
|
441
|
-
const actual = sql`
|
|
442
|
-
SELECT *
|
|
443
|
-
FROM table_name
|
|
444
|
-
WHERE column_name IN (
|
|
445
|
-
${[
|
|
446
|
-
true,
|
|
447
|
-
null,
|
|
448
|
-
undefined,
|
|
449
|
-
NaN,
|
|
450
|
-
Infinity,
|
|
451
|
-
42,
|
|
452
|
-
'42',
|
|
453
|
-
"4'2",
|
|
454
|
-
/42/,
|
|
455
|
-
new Date(323325000000),
|
|
456
|
-
]}
|
|
457
|
-
)`
|
|
458
|
-
|
|
459
|
-
const expected = `
|
|
460
|
-
SELECT *
|
|
461
|
-
FROM table_name
|
|
462
|
-
WHERE column_name IN (
|
|
463
|
-
b'1', NULL, NULL, NULL, NULL, 42, '42', '4''2', NULL, '1980-03-31 04:30:00'
|
|
464
|
-
)`
|
|
465
|
-
|
|
466
|
-
expect(actual).to.equal(expected)
|
|
467
|
-
```
|
|
468
|
-
|
|
469
|
-
### createPluralRules
|
|
470
|
-
|
|
471
|
-
```ts
|
|
472
|
-
const createPluralRules: (locale: string, forms: Partial<Record<Intl.LDMLPluralRule, string>>) => (value: number) => string;
|
|
473
|
-
```
|
|
474
|
-
|
|
475
|
-
Creates a function that
|
|
476
|
-
returns the appropriate noun form
|
|
477
|
-
based on a numeric value using `Intl.PluralRules`.
|
|
478
|
-
|
|
479
|
-
Different languages have different plural rules.
|
|
480
|
-
The `Intl.PluralRules` API provides locale-aware
|
|
481
|
-
plural category selection.
|
|
482
|
-
Possible categories are:
|
|
483
|
-
|
|
484
|
-
- `zero`: for zero items
|
|
485
|
-
(used in some languages like Arabic, Latvian)
|
|
486
|
-
- `one`: for singular (e.g., 1 item)
|
|
487
|
-
- `two`: for dual
|
|
488
|
-
(used in some languages like Arabic, Hebrew)
|
|
489
|
-
- `few`: for small plurals (e.g., 2-4 in Polish)
|
|
490
|
-
- `many`: for larger plurals (e.g., 5-21 in Polish)
|
|
491
|
-
- `other`: fallback category (used by all languages)
|
|
492
|
-
|
|
493
|
-
#### locale
|
|
494
|
-
|
|
495
|
-
A BCP 47 language tag (e.g., `pl`, `en`).
|
|
496
|
-
|
|
497
|
-
#### forms
|
|
498
|
-
|
|
499
|
-
An object mapping plural categories to noun forms.
|
|
500
|
-
Not all categories need to be provided;
|
|
501
|
-
if a category is missing,
|
|
502
|
-
the function falls back to `other`,
|
|
503
|
-
then to an empty string.
|
|
504
|
-
|
|
505
|
-
#### Returns
|
|
506
|
-
|
|
507
|
-
A function that takes a numeric value
|
|
508
|
-
and returns the appropriate noun form.
|
|
509
|
-
|
|
510
|
-
#### Usage Examples
|
|
511
|
-
|
|
512
|
-
```ts
|
|
513
|
-
const auto = createPluralRules('pl', {
|
|
514
|
-
one: 'auto',
|
|
515
|
-
few: 'auta',
|
|
516
|
-
other: 'aut',
|
|
517
|
-
})
|
|
518
|
-
|
|
519
|
-
expect(auto(0)).to.equal('aut')
|
|
520
|
-
expect(auto(1)).to.equal('auto')
|
|
521
|
-
expect(auto(17)).to.equal('aut')
|
|
522
|
-
expect(auto(42)).to.equal('auta')
|
|
523
|
-
|
|
524
|
-
const car = createPluralRules('en', {
|
|
525
|
-
one: 'car',
|
|
526
|
-
other: 'cars',
|
|
527
|
-
})
|
|
528
|
-
|
|
529
|
-
expect(car(0)).to.equal('cars')
|
|
530
|
-
expect(car(1)).to.equal('car')
|
|
531
|
-
expect(car(17)).to.equal('cars')
|
|
532
|
-
expect(car(42)).to.equal('cars')
|
|
533
|
-
|
|
534
|
-
const empty = createPluralRules('en', {})
|
|
535
|
-
|
|
536
|
-
expect(empty(0)).to.equal('')
|
|
537
|
-
expect(empty(1)).to.equal('')
|
|
538
|
-
expect(empty(17)).to.equal('')
|
|
539
|
-
expect(empty(42)).to.equal('')
|
|
540
|
-
```
|
|
541
|
-
|
|
542
134
|
### createTokenizer
|
|
543
135
|
|
|
544
136
|
```ts
|
|
@@ -561,9 +153,9 @@ For unmatched text between patterns,
|
|
|
561
153
|
|
|
562
154
|
An array of tuples `[metadata, pattern]` where:
|
|
563
155
|
- `metadata`: arbitrary data (e.g., a CSS class name)
|
|
564
|
-
|
|
156
|
+
passed to `decorator` when the pattern matches.
|
|
565
157
|
- `pattern`: a `string` or `RegExp`
|
|
566
|
-
|
|
158
|
+
to match against the input.
|
|
567
159
|
|
|
568
160
|
#### Returns
|
|
569
161
|
|
|
@@ -573,11 +165,11 @@ and returns an array of decorated tokens.
|
|
|
573
165
|
#### Remarks
|
|
574
166
|
|
|
575
167
|
1. Matches starting at an earlier position
|
|
576
|
-
|
|
168
|
+
take precedence.
|
|
577
169
|
2. Among matches at the same position,
|
|
578
|
-
|
|
170
|
+
the longer one wins.
|
|
579
171
|
3. Among matches of the same position and length,
|
|
580
|
-
|
|
172
|
+
the one defined earlier wins.
|
|
581
173
|
|
|
582
174
|
#### Usage Examples
|
|
583
175
|
|
|
@@ -803,14 +395,14 @@ If a `Node`, that node is modified in place.
|
|
|
803
395
|
|
|
804
396
|
Additional arguments processed as follows:
|
|
805
397
|
- `Object`: maps attributes/properties.
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
398
|
+
Keys starting with `$` set element properties
|
|
399
|
+
(without the `$` prefix);
|
|
400
|
+
other keys set attributes via `setAttribute`.
|
|
401
|
+
A value of `false` removes the attribute.
|
|
810
402
|
- `null`/`undefined`: ignored.
|
|
811
403
|
- `Node`: appended as a child.
|
|
812
404
|
- `string`/`number`: converted to a `Text` node
|
|
813
|
-
|
|
405
|
+
and appended.
|
|
814
406
|
- [`HArgs`](#HArgs) array: processed recursively.
|
|
815
407
|
|
|
816
408
|
#### Returns
|
|
@@ -1215,93 +807,14 @@ The JSON string to parse.
|
|
|
1215
807
|
|
|
1216
808
|
The parsed value with handler substitutions applied.
|
|
1217
809
|
|
|
1218
|
-
#### Usage Examples
|
|
1219
|
-
|
|
1220
|
-
```ts
|
|
1221
|
-
const handlers = {
|
|
1222
|
-
$add: (a: number, b: number) => a + b,
|
|
1223
|
-
$hello: (name: string) => `Hello ${name}!`,
|
|
1224
|
-
$foo: () => 'bar',
|
|
1225
|
-
}
|
|
1226
|
-
|
|
1227
|
-
const actual1 = jsOnParse(
|
|
1228
|
-
handlers,
|
|
1229
|
-
`[
|
|
1230
|
-
{
|
|
1231
|
-
"$add": [1, 2]
|
|
1232
|
-
},
|
|
1233
|
-
{
|
|
1234
|
-
"$hello": ["World"]
|
|
1235
|
-
},
|
|
1236
|
-
{
|
|
1237
|
-
"nested": {
|
|
1238
|
-
"$hello": ["nested World"]
|
|
1239
|
-
},
|
|
1240
|
-
"one": 1,
|
|
1241
|
-
"two": 2
|
|
1242
|
-
},
|
|
1243
|
-
{
|
|
1244
|
-
"$foo": []
|
|
1245
|
-
},
|
|
1246
|
-
{
|
|
1247
|
-
"$foo": ["The parent object does not have exactly one property!"],
|
|
1248
|
-
"one": 1,
|
|
1249
|
-
"two": 2
|
|
1250
|
-
}
|
|
1251
|
-
]`
|
|
1252
|
-
)
|
|
1253
|
-
|
|
1254
|
-
expect(actual1).to.deep.equal([
|
|
1255
|
-
3,
|
|
1256
|
-
'Hello World!',
|
|
1257
|
-
{
|
|
1258
|
-
nested: 'Hello nested World!',
|
|
1259
|
-
one: 1,
|
|
1260
|
-
two: 2,
|
|
1261
|
-
},
|
|
1262
|
-
'bar',
|
|
1263
|
-
{
|
|
1264
|
-
$foo: [
|
|
1265
|
-
'The parent object does not have ' +
|
|
1266
|
-
'exactly one property!',
|
|
1267
|
-
],
|
|
1268
|
-
one: 1,
|
|
1269
|
-
two: 2,
|
|
1270
|
-
},
|
|
1271
|
-
])
|
|
1272
|
-
|
|
1273
|
-
const actual2 = jsOnParse(
|
|
1274
|
-
{ $notFunc: 'handler not being a function' } as any,
|
|
1275
|
-
'{"$notFunc": [1, 2]}'
|
|
1276
|
-
)
|
|
1277
|
-
|
|
1278
|
-
expect(actual2).to.deep.equal({ $notFunc: [1, 2] })
|
|
1279
|
-
|
|
1280
|
-
const actual3 = jsOnParse(
|
|
1281
|
-
handlers,
|
|
1282
|
-
'{"$unknown_handler_key": [1, 2]}'
|
|
1283
|
-
)
|
|
1284
|
-
|
|
1285
|
-
expect(actual3).to.deep.equal({
|
|
1286
|
-
$unknown_handler_key: [1, 2],
|
|
1287
|
-
})
|
|
1288
|
-
|
|
1289
|
-
const actual4 = jsOnParse(
|
|
1290
|
-
handlers,
|
|
1291
|
-
'{"$add": {"not": "array"}}'
|
|
1292
|
-
)
|
|
1293
|
-
|
|
1294
|
-
expect(actual4).to.deep.equal({ $add: { not: 'array' } })
|
|
1295
|
-
```
|
|
1296
|
-
|
|
1297
810
|
### monokai
|
|
1298
811
|
|
|
1299
812
|
```ts
|
|
1300
|
-
const monokai:
|
|
813
|
+
const monokai: CNode;
|
|
1301
814
|
```
|
|
1302
815
|
|
|
1303
816
|
A Monokai-inspired color scheme
|
|
1304
|
-
for use with the
|
|
817
|
+
for use with the {@link c} helper
|
|
1305
818
|
and [`nanolightTs`](#nanolightTs) tokenizer.
|
|
1306
819
|
|
|
1307
820
|
### nanolightTs
|
|
@@ -1330,11 +843,11 @@ const codeJs =
|
|
|
1330
843
|
"{ 42: 42 }['42'] /* 42 */"
|
|
1331
844
|
|
|
1332
845
|
expect(nanolightTs(codeJs)).to.deep.equal([
|
|
1333
|
-
['span', { class: '
|
|
846
|
+
['span', { class: 'keyword1' }, 'const'],
|
|
1334
847
|
' ',
|
|
1335
848
|
[
|
|
1336
849
|
'span',
|
|
1337
|
-
{ class: '
|
|
850
|
+
{ class: 'identifier4' },
|
|
1338
851
|
'answerToLifeTheUniverseAndEverything',
|
|
1339
852
|
],
|
|
1340
853
|
' ',
|
|
@@ -1428,7 +941,7 @@ expect(pick(ref, ['a', 'b'])).to.deep.equal({
|
|
|
1428
941
|
### rwd
|
|
1429
942
|
|
|
1430
943
|
```ts
|
|
1431
|
-
const rwd: (root:
|
|
944
|
+
const rwd: (root: CNode, selector: string, cellWidthPx: number, cellHeightPx: number, ...specs: [number, number?, number?][]) => void;
|
|
1432
945
|
```
|
|
1433
946
|
|
|
1434
947
|
A responsive web design helper
|
|
@@ -1436,7 +949,7 @@ that generates CSS rules for a grid-like layout.
|
|
|
1436
949
|
|
|
1437
950
|
#### root
|
|
1438
951
|
|
|
1439
|
-
The CSS root object to populate (see
|
|
952
|
+
The CSS root object to populate (see {@link c}).
|
|
1440
953
|
|
|
1441
954
|
#### selector
|
|
1442
955
|
|
|
@@ -1454,16 +967,16 @@ The base cell height in pixels.
|
|
|
1454
967
|
|
|
1455
968
|
An array of breakpoint specifications, each a tuple of:
|
|
1456
969
|
- `maxWidth`: maximum number of cells per row
|
|
1457
|
-
|
|
970
|
+
(defines the viewport breakpoint).
|
|
1458
971
|
- `width` (optional, default `1`):
|
|
1459
|
-
|
|
972
|
+
number of horizontal cells the element spans.
|
|
1460
973
|
- `height` (optional, default `1`):
|
|
1461
|
-
|
|
974
|
+
number of vertical cells the element spans.
|
|
1462
975
|
|
|
1463
976
|
#### Usage Examples
|
|
1464
977
|
|
|
1465
978
|
```ts
|
|
1466
|
-
const style:
|
|
979
|
+
const style: CNode = {
|
|
1467
980
|
body: {
|
|
1468
981
|
margin: 0,
|
|
1469
982
|
},
|
|
@@ -1531,14 +1044,14 @@ If a `Node`, that node is modified in place.
|
|
|
1531
1044
|
|
|
1532
1045
|
Additional arguments processed as follows:
|
|
1533
1046
|
- `Object`: maps attributes/properties.
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1047
|
+
Keys starting with `$` set element properties
|
|
1048
|
+
(without the `$` prefix);
|
|
1049
|
+
other keys set attributes via `setAttributeNS`.
|
|
1050
|
+
A value of `false` removes the attribute.
|
|
1538
1051
|
- `null`/`undefined`: ignored.
|
|
1539
1052
|
- `Node`: appended as a child.
|
|
1540
1053
|
- `string`/`number`: converted to a `Text` node
|
|
1541
|
-
|
|
1054
|
+
and appended.
|
|
1542
1055
|
- [`HArgs`](#HArgs) array: processed recursively.
|
|
1543
1056
|
|
|
1544
1057
|
#### Returns
|