-
Notifications
You must be signed in to change notification settings - Fork 0
/
color.js
123 lines (110 loc) · 2.84 KB
/
color.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
import { release } from 'node:os';
import { env, platform } from 'node:process';
import { inspect } from 'node:util';
export class ColorUtils {
/**
* @type {boolean}
*/
#enabled;
/**
* @type {[string, string]}
*/
#brackets;
/**
* @param {{ enabled?: boolean; brackets?: [string, string] }} [options]
*/
constructor(options) {
this.#enabled = typeof options?.enabled === 'boolean' ? options.enabled : checkColorSupport();
this.#brackets = Array.isArray(options?.brackets) ? options.brackets : ['[', ']'];
}
get enabled() {
return this.#enabled;
}
/**
* @param {boolean} value
*/
set enabled(value) {
this.#enabled = value;
}
/**
* @type {(text: string, format?: string) => string}
*/
style = (text, format = '') => {
if (!this.enabled) return text;
return styleText(format.trim().split(/\s+/g), text);
};
/**
* @type {(text: string, format?: string) => string}
*/
brackets = (text, format = 'dim,,dim') => {
return this.sequence([this.#brackets[0], text, this.#brackets[1]], format);
};
/**
* @type {(parts: string[], format?: string) => string}
*/
sequence = (parts, format = '') => {
if (!format || !this.enabled) {
return parts.join('');
}
const formats = format.split(',');
return parts
.map((part, index) => (formats[index] ? this.style(part, formats[index]) : part))
.join('');
};
/**
* @type {(input: string) => string}
*/
strip = stripStyle;
}
/**
* @returns {boolean}
*/
function checkColorSupport() {
const getEnv = (key = '') => env[key]?.toLowerCase() || '';
if (typeof env['NO_COLOR'] === 'string') {
const forceColor = getEnv('FORCE_COLOR');
return forceColor === 'true' || /^\d$/.test(forceColor);
}
if (platform === 'win32') {
// Logic borrowed from supports-color.
// Windows 10 build 10586 is the first release that supports 256 colors.
const [major, _, build] = release().split('.');
return Number(major) >= 10 && Number(build) >= 10_586;
} else {
const term = getEnv('TERM');
const colorterm = getEnv('COLORTERM');
return (
colorterm === 'truecolor' ||
term === 'xterm-256color' ||
term === 'xterm-16color' ||
term === 'xterm-color'
);
}
}
/**
* Basic implementation of 'node:util' styleText to support Node 18 + Deno.
* @param {string | string[]} format
* @param {string} text
* @returns {string}
*/
function styleText(format, text) {
let before = '';
let after = '';
for (const style of Array.isArray(format) ? format : [format]) {
const codes = inspect.colors[style.trim()];
if (!codes) continue;
before = `${before}\x1b[${codes[0]}m`;
after = `\x1b[${codes[1]}m${after}`;
}
return `${before}${text}${after}`;
}
/**
* @param {string} input
* @returns {string}
*/
export function stripStyle(input) {
if (typeof input === 'string' && input.includes('\x1b[')) {
return input.replace(/\x1b\[\d+m/g, '');
}
return input;
}