UNPKG

5.11 kBJavaScriptView Raw
1const ANSI_BACKGROUND_OFFSET = 10;
2
3const wrapAnsi16 = (offset = 0) => code => `\u001B[${code + offset}m`;
4
5const wrapAnsi256 = (offset = 0) => code => `\u001B[${38 + offset};5;${code}m`;
6
7const wrapAnsi16m = (offset = 0) => (red, green, blue) => `\u001B[${38 + offset};2;${red};${green};${blue}m`;
8
9function assembleStyles() {
10 const codes = new Map();
11 const styles = {
12 modifier: {
13 reset: [0, 0],
14 // 21 isn't widely supported and 22 does the same thing
15 bold: [1, 22],
16 dim: [2, 22],
17 italic: [3, 23],
18 underline: [4, 24],
19 overline: [53, 55],
20 inverse: [7, 27],
21 hidden: [8, 28],
22 strikethrough: [9, 29],
23 },
24 color: {
25 black: [30, 39],
26 red: [31, 39],
27 green: [32, 39],
28 yellow: [33, 39],
29 blue: [34, 39],
30 magenta: [35, 39],
31 cyan: [36, 39],
32 white: [37, 39],
33
34 // Bright color
35 blackBright: [90, 39],
36 redBright: [91, 39],
37 greenBright: [92, 39],
38 yellowBright: [93, 39],
39 blueBright: [94, 39],
40 magentaBright: [95, 39],
41 cyanBright: [96, 39],
42 whiteBright: [97, 39],
43 },
44 bgColor: {
45 bgBlack: [40, 49],
46 bgRed: [41, 49],
47 bgGreen: [42, 49],
48 bgYellow: [43, 49],
49 bgBlue: [44, 49],
50 bgMagenta: [45, 49],
51 bgCyan: [46, 49],
52 bgWhite: [47, 49],
53
54 // Bright color
55 bgBlackBright: [100, 49],
56 bgRedBright: [101, 49],
57 bgGreenBright: [102, 49],
58 bgYellowBright: [103, 49],
59 bgBlueBright: [104, 49],
60 bgMagentaBright: [105, 49],
61 bgCyanBright: [106, 49],
62 bgWhiteBright: [107, 49],
63 },
64 };
65
66 // Alias bright black as gray (and grey)
67 styles.color.gray = styles.color.blackBright;
68 styles.bgColor.bgGray = styles.bgColor.bgBlackBright;
69 styles.color.grey = styles.color.blackBright;
70 styles.bgColor.bgGrey = styles.bgColor.bgBlackBright;
71
72 for (const [groupName, group] of Object.entries(styles)) {
73 for (const [styleName, style] of Object.entries(group)) {
74 styles[styleName] = {
75 open: `\u001B[${style[0]}m`,
76 close: `\u001B[${style[1]}m`,
77 };
78
79 group[styleName] = styles[styleName];
80
81 codes.set(style[0], style[1]);
82 }
83
84 Object.defineProperty(styles, groupName, {
85 value: group,
86 enumerable: false,
87 });
88 }
89
90 Object.defineProperty(styles, 'codes', {
91 value: codes,
92 enumerable: false,
93 });
94
95 styles.color.close = '\u001B[39m';
96 styles.bgColor.close = '\u001B[49m';
97
98 styles.color.ansi = wrapAnsi16();
99 styles.color.ansi256 = wrapAnsi256();
100 styles.color.ansi16m = wrapAnsi16m();
101 styles.bgColor.ansi = wrapAnsi16(ANSI_BACKGROUND_OFFSET);
102 styles.bgColor.ansi256 = wrapAnsi256(ANSI_BACKGROUND_OFFSET);
103 styles.bgColor.ansi16m = wrapAnsi16m(ANSI_BACKGROUND_OFFSET);
104
105 // From https://github.com/Qix-/color-convert/blob/3f0e0d4e92e235796ccb17f6e85c72094a651f49/conversions.js
106 Object.defineProperties(styles, {
107 rgbToAnsi256: {
108 value: (red, green, blue) => {
109 // We use the extended greyscale palette here, with the exception of
110 // black and white. normal palette only has 4 greyscale shades.
111 if (red === green && green === blue) {
112 if (red < 8) {
113 return 16;
114 }
115
116 if (red > 248) {
117 return 231;
118 }
119
120 return Math.round(((red - 8) / 247) * 24) + 232;
121 }
122
123 return 16
124 + (36 * Math.round(red / 255 * 5))
125 + (6 * Math.round(green / 255 * 5))
126 + Math.round(blue / 255 * 5);
127 },
128 enumerable: false,
129 },
130 hexToRgb: {
131 value: hex => {
132 const matches = /[a-f\d]{6}|[a-f\d]{3}/i.exec(hex.toString(16));
133 if (!matches) {
134 return [0, 0, 0];
135 }
136
137 let [colorString] = matches;
138
139 if (colorString.length === 3) {
140 colorString = [...colorString].map(character => character + character).join('');
141 }
142
143 const integer = Number.parseInt(colorString, 16);
144
145 return [
146 /* eslint-disable no-bitwise */
147 (integer >> 16) & 0xFF,
148 (integer >> 8) & 0xFF,
149 integer & 0xFF,
150 /* eslint-enable no-bitwise */
151 ];
152 },
153 enumerable: false,
154 },
155 hexToAnsi256: {
156 value: hex => styles.rgbToAnsi256(...styles.hexToRgb(hex)),
157 enumerable: false,
158 },
159 ansi256ToAnsi: {
160 value: code => {
161 if (code < 8) {
162 return 30 + code;
163 }
164
165 if (code < 16) {
166 return 90 + (code - 8);
167 }
168
169 let red;
170 let green;
171 let blue;
172
173 if (code >= 232) {
174 red = (((code - 232) * 10) + 8) / 255;
175 green = red;
176 blue = red;
177 } else {
178 code -= 16;
179
180 const remainder = code % 36;
181
182 red = Math.floor(code / 36) / 5;
183 green = Math.floor(remainder / 6) / 5;
184 blue = (remainder % 6) / 5;
185 }
186
187 const value = Math.max(red, green, blue) * 2;
188
189 if (value === 0) {
190 return 30;
191 }
192
193 // eslint-disable-next-line no-bitwise
194 let result = 30 + ((Math.round(blue) << 2) | (Math.round(green) << 1) | Math.round(red));
195
196 if (value === 2) {
197 result += 60;
198 }
199
200 return result;
201 },
202 enumerable: false,
203 },
204 rgbToAnsi: {
205 value: (red, green, blue) => styles.ansi256ToAnsi(styles.rgbToAnsi256(red, green, blue)),
206 enumerable: false,
207 },
208 hexToAnsi: {
209 value: hex => styles.ansi256ToAnsi(styles.hexToAnsi256(hex)),
210 enumerable: false,
211 },
212 });
213
214 return styles;
215}
216
217const ansiStyles = assembleStyles();
218
219export default ansiStyles;