@ix-xs/node-comfort 1.2.0 → 2.0.0
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/CHANGELOG.md +190 -0
- package/README.md +136 -549
- package/index.js +80 -35
- package/package.json +160 -33
- package/src/Arr.js +887 -419
- package/src/Async.js +585 -0
- package/src/Cache.js +329 -0
- package/src/Checker.js +971 -539
- package/src/Cli.js +1034 -0
- package/src/Color.js +415 -0
- package/src/Crypto.js +707 -0
- package/src/Emitter.js +289 -0
- package/src/Env.js +555 -0
- package/src/FS.js +1483 -894
- package/src/Func.js +670 -353
- package/src/Http.js +538 -0
- package/src/Id.js +384 -201
- package/src/Logger.js +739 -378
- package/src/Num.js +844 -361
- package/src/Obj.js +855 -372
- package/src/SQLite.js +1308 -1005
- package/src/Schema.js +1586 -0
- package/src/Str.js +983 -436
- package/src/Sys.js +511 -0
- package/src/Time.js +1260 -422
- package/src/Utils.js +321 -325
- package/src/errors.js +292 -0
- package/types/index.d.ts +693 -526
- package/types/src/Arr.d.ts +455 -145
- package/types/src/Arr.d.ts.map +1 -1
- package/types/src/Async.d.ts +321 -0
- package/types/src/Async.d.ts.map +1 -0
- package/types/src/Cache.d.ts +195 -0
- package/types/src/Cache.d.ts.map +1 -0
- package/types/src/Checker.d.ts +501 -235
- package/types/src/Checker.d.ts.map +1 -1
- package/types/src/Cli.d.ts +582 -0
- package/types/src/Cli.d.ts.map +1 -0
- package/types/src/Color.d.ts +294 -0
- package/types/src/Color.d.ts.map +1 -0
- package/types/src/Crypto.d.ts +534 -0
- package/types/src/Crypto.d.ts.map +1 -0
- package/types/src/Emitter.d.ts +170 -0
- package/types/src/Emitter.d.ts.map +1 -0
- package/types/src/Env.d.ts +382 -0
- package/types/src/Env.d.ts.map +1 -0
- package/types/src/FS.d.ts +686 -238
- package/types/src/FS.d.ts.map +1 -1
- package/types/src/Func.d.ts +380 -135
- package/types/src/Func.d.ts.map +1 -1
- package/types/src/Http.d.ts +374 -0
- package/types/src/Http.d.ts.map +1 -0
- package/types/src/Id.d.ts +149 -68
- package/types/src/Id.d.ts.map +1 -1
- package/types/src/Logger.d.ts +363 -46
- package/types/src/Logger.d.ts.map +1 -1
- package/types/src/Num.d.ts +473 -137
- package/types/src/Num.d.ts.map +1 -1
- package/types/src/Obj.d.ts +390 -104
- package/types/src/Obj.d.ts.map +1 -1
- package/types/src/SQLite.d.ts +646 -309
- package/types/src/SQLite.d.ts.map +1 -1
- package/types/src/Schema.d.ts +940 -0
- package/types/src/Schema.d.ts.map +1 -0
- package/types/src/Str.d.ts +569 -166
- package/types/src/Str.d.ts.map +1 -1
- package/types/src/Sys.d.ts +269 -0
- package/types/src/Sys.d.ts.map +1 -0
- package/types/src/Time.d.ts +663 -204
- package/types/src/Time.d.ts.map +1 -1
- package/types/src/Utils.d.ts +154 -102
- package/types/src/Utils.d.ts.map +1 -1
- package/types/src/errors.d.ts +292 -0
- package/types/src/errors.d.ts.map +1 -0
- package/types/index.d.ts.map +0 -1
package/src/Color.js
ADDED
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Terminal colors you can chain, like `chalk`. Truecolor is supported and
|
|
5
|
+
* downgraded on older terminals. Colors switch off by themselves when the
|
|
6
|
+
* output isn't a terminal or `NO_COLOR` is set; `FORCE_COLOR=1|2|3` turns
|
|
7
|
+
* them back on.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* console.log(color.green.bold("✔ Done"), color.gray("in 1.2s"));
|
|
11
|
+
* console.log(color.bgHex("#1e1e2e").hex("#cba6f7")(" nc "));
|
|
12
|
+
* console.log(color.gradient("node-comfort", ["#ff5f6d", "#ffc371"]));
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Every named style.
|
|
17
|
+
* @typedef {"reset" | "bold" | "dim" | "italic" | "underline" | "overline" | "inverse" | "hidden" | "strikethrough"
|
|
18
|
+
* | "black" | "red" | "green" | "yellow" | "blue" | "magenta" | "cyan" | "white" | "gray" | "grey"
|
|
19
|
+
* | "blackBright" | "redBright" | "greenBright" | "yellowBright" | "blueBright" | "magentaBright" | "cyanBright" | "whiteBright"
|
|
20
|
+
* | "bgBlack" | "bgRed" | "bgGreen" | "bgYellow" | "bgBlue" | "bgMagenta" | "bgCyan" | "bgWhite" | "bgGray" | "bgGrey"
|
|
21
|
+
* | "bgBlackBright" | "bgRedBright" | "bgGreenBright" | "bgYellowBright" | "bgBlueBright" | "bgMagentaBright" | "bgCyanBright" | "bgWhiteBright"} StyleName
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* How many colors the terminal can show: `0` none, `1` 16, `2` 256, `3` millions.
|
|
26
|
+
* @typedef {0 | 1 | 2 | 3} ColorLevel
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* The named styles. Each one can be called on text or chained with others:
|
|
31
|
+
* `color.red("x")`, `color.red.bold.underline("x")`.
|
|
32
|
+
* @typedef {object} ColorStyleProps
|
|
33
|
+
* @property {ColorChain} reset Removes all styles.
|
|
34
|
+
* @property {ColorChain} bold Bold text.
|
|
35
|
+
* @property {ColorChain} dim Faint text.
|
|
36
|
+
* @property {ColorChain} italic Italic text, where the terminal supports it.
|
|
37
|
+
* @property {ColorChain} underline Underlined text.
|
|
38
|
+
* @property {ColorChain} overline Line above the text, where supported.
|
|
39
|
+
* @property {ColorChain} inverse Swaps text and background colors.
|
|
40
|
+
* @property {ColorChain} hidden Invisible text, still copyable.
|
|
41
|
+
* @property {ColorChain} strikethrough Crossed-out text.
|
|
42
|
+
* @property {ColorChain} black Black text.
|
|
43
|
+
* @property {ColorChain} red Red text.
|
|
44
|
+
* @property {ColorChain} green Green text.
|
|
45
|
+
* @property {ColorChain} yellow Yellow text.
|
|
46
|
+
* @property {ColorChain} blue Blue text.
|
|
47
|
+
* @property {ColorChain} magenta Magenta text.
|
|
48
|
+
* @property {ColorChain} cyan Cyan text.
|
|
49
|
+
* @property {ColorChain} white White text.
|
|
50
|
+
* @property {ColorChain} gray Gray text.
|
|
51
|
+
* @property {ColorChain} grey Same as `gray`.
|
|
52
|
+
* @property {ColorChain} blackBright Bright black text.
|
|
53
|
+
* @property {ColorChain} redBright Bright red text.
|
|
54
|
+
* @property {ColorChain} greenBright Bright green text.
|
|
55
|
+
* @property {ColorChain} yellowBright Bright yellow text.
|
|
56
|
+
* @property {ColorChain} blueBright Bright blue text.
|
|
57
|
+
* @property {ColorChain} magentaBright Bright magenta text.
|
|
58
|
+
* @property {ColorChain} cyanBright Bright cyan text.
|
|
59
|
+
* @property {ColorChain} whiteBright Bright white text.
|
|
60
|
+
* @property {ColorChain} bgBlack Black background.
|
|
61
|
+
* @property {ColorChain} bgRed Red background.
|
|
62
|
+
* @property {ColorChain} bgGreen Green background.
|
|
63
|
+
* @property {ColorChain} bgYellow Yellow background.
|
|
64
|
+
* @property {ColorChain} bgBlue Blue background.
|
|
65
|
+
* @property {ColorChain} bgMagenta Magenta background.
|
|
66
|
+
* @property {ColorChain} bgCyan Cyan background.
|
|
67
|
+
* @property {ColorChain} bgWhite White background.
|
|
68
|
+
* @property {ColorChain} bgGray Gray background.
|
|
69
|
+
* @property {ColorChain} bgGrey Same as `bgGray`.
|
|
70
|
+
* @property {ColorChain} bgBlackBright Bright black background.
|
|
71
|
+
* @property {ColorChain} bgRedBright Bright red background.
|
|
72
|
+
* @property {ColorChain} bgGreenBright Bright green background.
|
|
73
|
+
* @property {ColorChain} bgYellowBright Bright yellow background.
|
|
74
|
+
* @property {ColorChain} bgBlueBright Bright blue background.
|
|
75
|
+
* @property {ColorChain} bgMagentaBright Bright magenta background.
|
|
76
|
+
* @property {ColorChain} bgCyanBright Bright cyan background.
|
|
77
|
+
* @property {ColorChain} bgWhiteBright Bright white background.
|
|
78
|
+
*/
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* The named styles, read-only.
|
|
82
|
+
* @typedef {Readonly<ColorStyleProps>} ColorStyles
|
|
83
|
+
*/
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* A style you can call on text, or chain with more styles.
|
|
87
|
+
*
|
|
88
|
+
* @typedef {((...text: unknown[]) => string) & ColorStyles & ColorMethods} ColorChain
|
|
89
|
+
*/
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Custom colors, available on every chain.
|
|
93
|
+
* @typedef {object} ColorMethods
|
|
94
|
+
* @property {(r: number, g: number, b: number) => ColorChain} rgb Text color from red, green and blue (0-255).
|
|
95
|
+
* @property {(r: number, g: number, b: number) => ColorChain} bgRgb Background from red, green and blue (0-255).
|
|
96
|
+
* @property {(hex: string) => ColorChain} hex Text color from a hex code like `"#f80"`.
|
|
97
|
+
* @property {(hex: string) => ColorChain} bgHex Background from a hex code.
|
|
98
|
+
* @property {(code: number) => ColorChain} ansi256 Text color from the 256-color palette.
|
|
99
|
+
* @property {(code: number) => ColorChain} bgAnsi256 Background from the 256-color palette.
|
|
100
|
+
*/
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* `nc.color`: every style and color, plus a few helpers.
|
|
104
|
+
*
|
|
105
|
+
* @typedef {ColorStyles & ColorMethods & ColorHelpers} ColorModule
|
|
106
|
+
*/
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Color helpers.
|
|
110
|
+
* @typedef {object} ColorHelpers
|
|
111
|
+
* @property {ColorLevel} level The current color level. Set it to force one; `0` turns colors off.
|
|
112
|
+
* @property {boolean} enabled Whether colors are being output.
|
|
113
|
+
* @property {(level?: ColorLevel) => ColorModule} create A separate instance with its own level, for another stream or for tests.
|
|
114
|
+
* @property {(stream?: NodeJS.WriteStream | { isTTY?: boolean }) => ColorLevel} detect The color level a stream supports, taking `NO_COLOR` and `FORCE_COLOR` into account.
|
|
115
|
+
* @property {(text: string) => string} strip Removes colors and other terminal escape codes.
|
|
116
|
+
* @property {(text: string) => number} width How many columns the text takes, ignoring colors and counting emoji and CJK as 2.
|
|
117
|
+
* @property {(text: string, url: string) => string} link A clickable link in terminals that support it, `"text (url)"` elsewhere.
|
|
118
|
+
* @property {(text: string, colors: string[]) => string} gradient Colors the text along a gradient of hex colors.
|
|
119
|
+
*/
|
|
120
|
+
|
|
121
|
+
/** @type {Record<string, [number, number]>} */
|
|
122
|
+
const _CODES = {
|
|
123
|
+
reset: [0, 0], bold: [1, 22], dim: [2, 22], italic: [3, 23], underline: [4, 24], overline: [53, 55],
|
|
124
|
+
inverse: [7, 27], hidden: [8, 28], strikethrough: [9, 29],
|
|
125
|
+
black: [30, 39], red: [31, 39], green: [32, 39], yellow: [33, 39], blue: [34, 39], magenta: [35, 39], cyan: [36, 39], white: [37, 39],
|
|
126
|
+
gray: [90, 39], grey: [90, 39], blackBright: [90, 39], redBright: [91, 39], greenBright: [92, 39], yellowBright: [93, 39],
|
|
127
|
+
blueBright: [94, 39], magentaBright: [95, 39], cyanBright: [96, 39], whiteBright: [97, 39],
|
|
128
|
+
bgBlack: [40, 49], bgRed: [41, 49], bgGreen: [42, 49], bgYellow: [43, 49], bgBlue: [44, 49], bgMagenta: [45, 49], bgCyan: [46, 49], bgWhite: [47, 49],
|
|
129
|
+
bgGray: [100, 49], bgGrey: [100, 49], bgBlackBright: [100, 49], bgRedBright: [101, 49], bgGreenBright: [102, 49], bgYellowBright: [103, 49],
|
|
130
|
+
bgBlueBright: [104, 49], bgMagentaBright: [105, 49], bgCyanBright: [106, 49], bgWhiteBright: [107, 49],
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* A style in a chain: fixed codes, or a color resolved at output time.
|
|
135
|
+
* @typedef {{ open: string, close: string } | { rgb: [number, number, number], bg: boolean } | { ansi256: number, bg: boolean }} StyleEntry
|
|
136
|
+
*/
|
|
137
|
+
|
|
138
|
+
const _ANSI_REGEX = /[\u001b\u009b](?:\][^\u0007\u001b]*(?:\u0007|\u001b\\)|[[\]()#;?]*(?:\d{1,4}(?:[;:]\d{0,4})*)?[\dA-PR-TZcf-nq-uy=><~])/g;
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* @param {string} hex
|
|
142
|
+
* @returns {[number, number, number]}
|
|
143
|
+
*/
|
|
144
|
+
const _hexToRgb = (hex) => {
|
|
145
|
+
const clean = String(hex).replace(/^#/, "");
|
|
146
|
+
const full = clean.length === 3 || clean.length === 4 ? [...clean.slice(0, 3)].map((c) => c + c).join("") : clean.slice(0, 6);
|
|
147
|
+
if (!/^[0-9a-f]{6}$/i.test(full)) throw new TypeError(`Invalid hex color "${hex}"`);
|
|
148
|
+
return [parseInt(full.slice(0, 2), 16), parseInt(full.slice(2, 4), 16), parseInt(full.slice(4, 6), 16)];
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* @param {number} r
|
|
153
|
+
* @param {number} g
|
|
154
|
+
* @param {number} b
|
|
155
|
+
* @returns {number}
|
|
156
|
+
*/
|
|
157
|
+
const _rgbToAnsi256 = (r, g, b) => {
|
|
158
|
+
if (r === g && g === b) {
|
|
159
|
+
if (r < 8) return 16;
|
|
160
|
+
if (r > 248) return 231;
|
|
161
|
+
return Math.round(((r - 8) / 247) * 24) + 232;
|
|
162
|
+
}
|
|
163
|
+
return 16 + 36 * Math.round((r / 255) * 5) + 6 * Math.round((g / 255) * 5) + Math.round((b / 255) * 5);
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* @param {number} code
|
|
168
|
+
* @returns {[number, number, number]}
|
|
169
|
+
*/
|
|
170
|
+
const _ansi256ToRgb = (code) => {
|
|
171
|
+
if (code < 16) {
|
|
172
|
+
const base = [[0, 0, 0], [205, 0, 0], [0, 205, 0], [205, 205, 0], [0, 0, 238], [205, 0, 205], [0, 205, 205], [229, 229, 229],
|
|
173
|
+
[127, 127, 127], [255, 0, 0], [0, 255, 0], [255, 255, 0], [92, 92, 255], [255, 0, 255], [0, 255, 255], [255, 255, 255]];
|
|
174
|
+
return /** @type {[number, number, number]} */ (base[code]);
|
|
175
|
+
}
|
|
176
|
+
if (code >= 232) {
|
|
177
|
+
const v = (code - 232) * 10 + 8;
|
|
178
|
+
return [v, v, v];
|
|
179
|
+
}
|
|
180
|
+
const n = code - 16;
|
|
181
|
+
const step = (/** @type {number} */ x) => (x === 0 ? 0 : x * 40 + 55);
|
|
182
|
+
return [step(Math.floor(n / 36)), step(Math.floor((n % 36) / 6)), step(n % 6)];
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* @param {number} r
|
|
187
|
+
* @param {number} g
|
|
188
|
+
* @param {number} b
|
|
189
|
+
* @returns {number} A 16-color foreground code (30-37, 90-97).
|
|
190
|
+
*/
|
|
191
|
+
const _rgbToAnsi16 = (r, g, b) => {
|
|
192
|
+
const value = Math.round((Math.max(r, g, b) / 255) * 2);
|
|
193
|
+
if (value === 0) return 30;
|
|
194
|
+
const code = 30 + ((Math.round(b / 255) << 2) | (Math.round(g / 255) << 1) | Math.round(r / 255));
|
|
195
|
+
return value === 2 ? code + 60 : code;
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Resolves a style entry into escape codes for a given level.
|
|
200
|
+
* @param {StyleEntry} entry
|
|
201
|
+
* @param {ColorLevel} level
|
|
202
|
+
* @returns {{ open: string, close: string }}
|
|
203
|
+
*/
|
|
204
|
+
const _resolve = (entry, level) => {
|
|
205
|
+
if ("open" in entry) return entry;
|
|
206
|
+
const close = entry.bg ? "\u001b[49m" : "\u001b[39m";
|
|
207
|
+
const rgb = "rgb" in entry ? entry.rgb : _ansi256ToRgb(entry.ansi256);
|
|
208
|
+
if (level >= 3 && "rgb" in entry) return { open: `\u001b[${entry.bg ? 48 : 38};2;${rgb[0]};${rgb[1]};${rgb[2]}m`, close };
|
|
209
|
+
if (level >= 2) {
|
|
210
|
+
const code = "ansi256" in entry ? entry.ansi256 : _rgbToAnsi256(...rgb);
|
|
211
|
+
return { open: `\u001b[${entry.bg ? 48 : 38};5;${code}m`, close };
|
|
212
|
+
}
|
|
213
|
+
const code = _rgbToAnsi16(...rgb) + (entry.bg ? 10 : 0);
|
|
214
|
+
return { open: `\u001b[${code}m`, close };
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* @param {NodeJS.WriteStream | { isTTY?: boolean } | undefined} stream
|
|
219
|
+
* @returns {ColorLevel}
|
|
220
|
+
*/
|
|
221
|
+
const _detect = (stream = process.stdout) => {
|
|
222
|
+
const env = process.env;
|
|
223
|
+
const argv = process.argv;
|
|
224
|
+
if (argv.includes("--no-color") || argv.includes("--no-colors") || argv.includes("--color=false")) return 0;
|
|
225
|
+
if (env.NO_COLOR !== undefined && env.NO_COLOR !== "") return 0;
|
|
226
|
+
if (env.NODE_DISABLE_COLORS !== undefined && env.NODE_DISABLE_COLORS !== "") return 0;
|
|
227
|
+
if (env.FORCE_COLOR !== undefined) {
|
|
228
|
+
const value = env.FORCE_COLOR.toLowerCase();
|
|
229
|
+
if (value === "false" || value === "0") return 0;
|
|
230
|
+
if (value === "2") return 2;
|
|
231
|
+
if (value === "3") return 3;
|
|
232
|
+
return 1;
|
|
233
|
+
}
|
|
234
|
+
if (argv.includes("--color=16m") || argv.includes("--color=full") || argv.includes("--color=truecolor")) return 3;
|
|
235
|
+
if (argv.includes("--color=256")) return 2;
|
|
236
|
+
const forced = argv.includes("--color") || argv.includes("--colors") || argv.includes("--color=true") || argv.includes("--color=always");
|
|
237
|
+
if (env.TERM === "dumb" && !forced) return 0;
|
|
238
|
+
const tty = /** @type {NodeJS.WriteStream | undefined} */ (stream && "isTTY" in stream && stream.isTTY ? stream : undefined);
|
|
239
|
+
if (tty && typeof tty.getColorDepth === "function") {
|
|
240
|
+
const depth = tty.getColorDepth();
|
|
241
|
+
if (depth >= 24) return 3;
|
|
242
|
+
if (depth >= 8) return 2;
|
|
243
|
+
if (depth >= 4) return 1;
|
|
244
|
+
return forced ? 1 : 0;
|
|
245
|
+
}
|
|
246
|
+
if (env.CI !== undefined) {
|
|
247
|
+
if (env.GITHUB_ACTIONS || env.GITEA_ACTIONS) return 3;
|
|
248
|
+
if (["TRAVIS", "CIRCLECI", "APPVEYOR", "GITLAB_CI", "BUILDKITE", "DRONE"].some((name) => name in env)) return 1;
|
|
249
|
+
}
|
|
250
|
+
return forced ? 1 : 0;
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Terminal width of one grapheme.
|
|
255
|
+
* @param {string} grapheme
|
|
256
|
+
* @returns {number}
|
|
257
|
+
*/
|
|
258
|
+
const _charWidth = (grapheme) => {
|
|
259
|
+
const cp = /** @type {number} */ (grapheme.codePointAt(0));
|
|
260
|
+
if (cp < 32 || (cp >= 0x7f && cp < 0xa0)) return 0;
|
|
261
|
+
if (/^\p{Mark}+$/u.test(grapheme)) return 0;
|
|
262
|
+
if (/\p{Emoji_Presentation}/u.test(grapheme) || grapheme.includes("\ufe0f")) return 2;
|
|
263
|
+
if (/[ᄀ-ᅟ⺀-〾ぁ-㏿㐀-䶿一-鿿ꀀ-가-힣豈-︰-﹏-⦆¢-₩\u{20000}-\u{3FFFD}]/u.test(grapheme)) return 2;
|
|
264
|
+
return 1;
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
/** @type {Intl.Segmenter | undefined} */
|
|
268
|
+
let _segmenter;
|
|
269
|
+
const _graphemeSegmenter = () => (_segmenter ??= new Intl.Segmenter(undefined, { granularity: "grapheme" }));
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* @param {string} text
|
|
273
|
+
* @returns {string}
|
|
274
|
+
*/
|
|
275
|
+
const strip = (text) => String(text).replace(_ANSI_REGEX, "");
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* @param {string} text
|
|
279
|
+
* @returns {number}
|
|
280
|
+
*/
|
|
281
|
+
const width = (text) => {
|
|
282
|
+
const plain = strip(text);
|
|
283
|
+
if (/^[\x20-\x7e]*$/.test(plain)) return plain.length;
|
|
284
|
+
let total = 0;
|
|
285
|
+
for (const { segment } of _graphemeSegmenter().segment(plain)) total += _charWidth(segment);
|
|
286
|
+
return total;
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
const _supportsHyperlinks = () => {
|
|
290
|
+
const env = process.env;
|
|
291
|
+
if (env.FORCE_HYPERLINK === "1") return true;
|
|
292
|
+
if (env.FORCE_HYPERLINK === "0") return false;
|
|
293
|
+
if (!process.stdout.isTTY) return false;
|
|
294
|
+
if (env.WT_SESSION || env.KONSOLE_VERSION || env.DOMTERM) return true;
|
|
295
|
+
if (["iTerm.app", "WezTerm", "vscode", "Hyper", "ghostty", "Tabby"].includes(env.TERM_PROGRAM ?? "")) return true;
|
|
296
|
+
if (env.VTE_VERSION && Number(env.VTE_VERSION) >= 5000) return true;
|
|
297
|
+
return false;
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Builds an independent color instance.
|
|
302
|
+
* @param {ColorLevel | undefined} forcedLevel
|
|
303
|
+
* @returns {ColorModule}
|
|
304
|
+
*/
|
|
305
|
+
const _createModule = (forcedLevel) => {
|
|
306
|
+
/** @type {ColorLevel | undefined} */
|
|
307
|
+
let level = forcedLevel;
|
|
308
|
+
const currentLevel = () => (level === undefined ? (level = _detect()) : level);
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* @param {StyleEntry[]} stack
|
|
312
|
+
* @param {string} text
|
|
313
|
+
* @returns {string}
|
|
314
|
+
*/
|
|
315
|
+
const apply = (stack, text) => {
|
|
316
|
+
const lvl = currentLevel();
|
|
317
|
+
if (lvl === 0 || !stack.length || text === "") return text;
|
|
318
|
+
const resolved = stack.map((entry) => _resolve(entry, lvl));
|
|
319
|
+
let result = text;
|
|
320
|
+
for (const { open, close } of resolved) {
|
|
321
|
+
if (close && result.includes(close)) result = result.split(close).join(close + open);
|
|
322
|
+
}
|
|
323
|
+
const opens = resolved.map((s) => s.open).join("");
|
|
324
|
+
const closes = resolved.map((s) => s.close).reverse().join("");
|
|
325
|
+
if (result.includes("\n")) result = result.replace(/\r?\n/g, (nl) => closes + nl + opens);
|
|
326
|
+
return opens + result + closes;
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
const STACK = Symbol("stack");
|
|
330
|
+
/** @type {any} */
|
|
331
|
+
const proto = Object.create(Function.prototype);
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* @param {StyleEntry[]} stack
|
|
335
|
+
* @returns {ColorChain}
|
|
336
|
+
*/
|
|
337
|
+
const chain = (stack) => {
|
|
338
|
+
/** @type {any} */
|
|
339
|
+
const fn = (/** @type {unknown[]} */ ...text) => apply(stack, text.map((t) => String(t)).join(" "));
|
|
340
|
+
Object.setPrototypeOf(fn, proto);
|
|
341
|
+
fn[STACK] = stack;
|
|
342
|
+
return fn;
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
for (const [name, [open, close]] of Object.entries(_CODES)) {
|
|
346
|
+
Object.defineProperty(proto, name, {
|
|
347
|
+
get() {
|
|
348
|
+
return chain([...(this[STACK] ?? []), { open: `\u001b[${open}m`, close: `\u001b[${close}m` }]);
|
|
349
|
+
},
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
/** @type {Record<string, (this: any, ...args: any[]) => ColorChain>} */
|
|
353
|
+
const methods = {
|
|
354
|
+
rgb(r, g, b) { return chain([...(this[STACK] ?? []), { rgb: [r, g, b], bg: false }]); },
|
|
355
|
+
bgRgb(r, g, b) { return chain([...(this[STACK] ?? []), { rgb: [r, g, b], bg: true }]); },
|
|
356
|
+
hex(hex) { return chain([...(this[STACK] ?? []), { rgb: _hexToRgb(hex), bg: false }]); },
|
|
357
|
+
bgHex(hex) { return chain([...(this[STACK] ?? []), { rgb: _hexToRgb(hex), bg: true }]); },
|
|
358
|
+
ansi256(code) { return chain([...(this[STACK] ?? []), { ansi256: code, bg: false }]); },
|
|
359
|
+
bgAnsi256(code) { return chain([...(this[STACK] ?? []), { ansi256: code, bg: true }]); },
|
|
360
|
+
};
|
|
361
|
+
for (const [name, method] of Object.entries(methods)) Object.defineProperty(proto, name, { value: method });
|
|
362
|
+
|
|
363
|
+
/** @type {any} */
|
|
364
|
+
const root = Object.create(proto);
|
|
365
|
+
root[STACK] = [];
|
|
366
|
+
Object.defineProperties(root, {
|
|
367
|
+
level: {
|
|
368
|
+
enumerable: true,
|
|
369
|
+
get: currentLevel,
|
|
370
|
+
set(/** @type {ColorLevel} */ value) {
|
|
371
|
+
level = /** @type {ColorLevel} */ (Math.max(0, Math.min(3, Math.floor(value))));
|
|
372
|
+
},
|
|
373
|
+
},
|
|
374
|
+
enabled: { enumerable: true, get: () => currentLevel() > 0 },
|
|
375
|
+
create: { enumerable: true, value: (/** @type {ColorLevel | undefined} */ lvl) => _createModule(lvl) },
|
|
376
|
+
detect: { enumerable: true, value: _detect },
|
|
377
|
+
strip: { enumerable: true, value: strip },
|
|
378
|
+
width: { enumerable: true, value: width },
|
|
379
|
+
link: {
|
|
380
|
+
enumerable: true,
|
|
381
|
+
value: (/** @type {string} */ text, /** @type {string} */ url) =>
|
|
382
|
+
currentLevel() > 0 && _supportsHyperlinks() ? `\u001b]8;;${url}\u0007${text}\u001b]8;;\u0007` : `${text} (${url})`,
|
|
383
|
+
},
|
|
384
|
+
gradient: {
|
|
385
|
+
enumerable: true,
|
|
386
|
+
value: (/** @type {string} */ text, /** @type {string[]} */ colors) => {
|
|
387
|
+
const stops = colors.map(_hexToRgb);
|
|
388
|
+
if (!stops.length) return String(text);
|
|
389
|
+
const graphemes = Array.from(_graphemeSegmenter().segment(String(text)), (s) => s.segment);
|
|
390
|
+
const visible = graphemes.filter((g) => g.trim()).length;
|
|
391
|
+
let index = 0;
|
|
392
|
+
return graphemes
|
|
393
|
+
.map((g) => {
|
|
394
|
+
if (!g.trim()) return g;
|
|
395
|
+
const t = visible <= 1 ? 0 : index++ / (visible - 1);
|
|
396
|
+
const pos = t * (stops.length - 1);
|
|
397
|
+
const i = Math.min(Math.floor(pos), stops.length - 2 < 0 ? 0 : stops.length - 2);
|
|
398
|
+
const a = stops[i];
|
|
399
|
+
const b = stops[Math.min(i + 1, stops.length - 1)];
|
|
400
|
+
const f = pos - i;
|
|
401
|
+
const mix = /** @type {[number, number, number]} */ (a.map((v, k) => Math.round(v + (b[k] - v) * f)));
|
|
402
|
+
return apply([{ rgb: mix, bg: false }], g);
|
|
403
|
+
})
|
|
404
|
+
.join("");
|
|
405
|
+
},
|
|
406
|
+
},
|
|
407
|
+
});
|
|
408
|
+
return root;
|
|
409
|
+
};
|
|
410
|
+
|
|
411
|
+
/** @type {ColorModule} */
|
|
412
|
+
const color = _createModule(undefined);
|
|
413
|
+
|
|
414
|
+
module.exports = color;
|
|
415
|
+
|