src/utils/colors.js 3.1 K raw
1
import tty from "node:tty";
2
3
// eslint-disable-next-line no-warning-comments
4
// TODO: Use a better method when it's added to Node.js (https://github.com/nodejs/node/pull/40240)
5
// Lots of optionals here to support Deno.
6
const hasColors = tty?.WriteStream?.prototype?.hasColors?.() ?? false;
7
8
const format = (open, close) => {
9
	if (!hasColors) {
10
		return (input) => input;
11
	}
12
13
	const openCode = `\u001B[${open}m`;
14
	const closeCode = `\u001B[${close}m`;
15
16
	return (input) => {
17
		const string = input + ""; // eslint-disable-line no-implicit-coercion -- This is faster.
18
		let index = string.indexOf(closeCode);
19
20
		if (index === -1) {
21
			// Note: Intentionally not using string interpolation for performance reasons.
22
			return openCode + string + closeCode;
23
		}
24
25
		// Handle nested colors.
26
27
		// We could have done this, but it's too slow (as of Node.js 22).
28
		// return openCode + string.replaceAll(closeCode, (close === 22 ? closeCode : '') + openCode) + closeCode;
29
30
		let result = openCode;
31
		let lastIndex = 0;
32
33
		// SGR 22 resets both bold (1) and dim (2). When we encounter a nested
34
		// close for styles that use 22, we need to re-open the outer style.
35
		const reopenOnNestedClose = close === 22;
36
		const replaceCode = (reopenOnNestedClose ? closeCode : "") + openCode;
37
38
		while (index !== -1) {
39
			result += string.slice(lastIndex, index) + replaceCode;
40
			lastIndex = index + closeCode.length;
41
			index = string.indexOf(closeCode, lastIndex);
42
		}
43
44
		result += string.slice(lastIndex) + closeCode;
45
46
		return result;
47
	};
48
};
49
50
export const reset = format(0, 0);
51
export const bold = format(1, 22);
52
export const dim = format(2, 22);
53
export const italic = format(3, 23);
54
export const underline = format(4, 24);
55
export const overline = format(53, 55);
56
export const inverse = format(7, 27);
57
export const hidden = format(8, 28);
58
export const strikethrough = format(9, 29);
59
60
export const black = format(30, 39);
61
export const red = format(31, 39);
62
export const green = format(32, 39);
63
export const yellow = format(33, 39);
64
export const blue = format(34, 39);
65
export const magenta = format(35, 39);
66
export const cyan = format(36, 39);
67
export const white = format(37, 39);
68
export const gray = format(90, 39);
69
70
export const bgBlack = format(40, 49);
71
export const bgRed = format(41, 49);
72
export const bgGreen = format(42, 49);
73
export const bgYellow = format(43, 49);
74
export const bgBlue = format(44, 49);
75
export const bgMagenta = format(45, 49);
76
export const bgCyan = format(46, 49);
77
export const bgWhite = format(47, 49);
78
export const bgGray = format(100, 49);
79
80
export const redBright = format(91, 39);
81
export const greenBright = format(92, 39);
82
export const yellowBright = format(93, 39);
83
export const blueBright = format(94, 39);
84
export const magentaBright = format(95, 39);
85
export const cyanBright = format(96, 39);
86
export const whiteBright = format(97, 39);
87
88
export const bgRedBright = format(101, 49);
89
export const bgGreenBright = format(102, 49);
90
export const bgYellowBright = format(103, 49);
91
export const bgBlueBright = format(104, 49);
92
export const bgMagentaBright = format(105, 49);
93
export const bgCyanBright = format(106, 49);
94
export const bgWhiteBright = format(107, 49);