| 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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266 |
1x
1x
1x
1x
1x
1x
1x
1x
1x
1x
534x
1x
223x
223x
223x
2x
2x
223x
223x
338x
292x
436x
213x
4x
1x
1x
1x
1x
1x
1x
1x
2x
213x
223x
223x
223x
223x
160x
223x
295x
295x
284x
282x
280x
278x
266x
254x
220x
174x
16x
15x
13x
9x
2x
295x
11x
11x
11x
11x
33x
33x
| /**
* @module libs/sys/clog
* @createdAt 2016-07-15
*
* @copyright Copyright (c) 2016 Zhonglei Qiu
* @license Licensed under the MIT license.
*/
var hasAnsiColorRegExp = require('../tty/stripAnsi').gre
var bgRE = /^(?:bg|background)(\w+)$/ // match: bgRed, bgYellow ...
var resetModifRE = /^(?:reset|r)(\w+)$/ // match: resetDim, resetItalic ...
var hexRE = /^[0-9a-f]{3}(?:[0-9a-f]{3})?$/ // match: ff00ff, f0f ...
var PREFIX = '\x1b['
var SUFFIX = 'm'
var RESET = PREFIX + '0' + SUFFIX
var MODIFIERS = { // reset
bold: 1, // 21 // 21 isn't widely supported and 22 does the same thing
faint: 2, // 22
gray: 2, // 22
dim: 2, // 22
italic: 3, // 23
underline: 4, // 24
reverse: 7 // 27
}
// 30-37 color; 40-47 background
// 90-97 high intensity color; 100-107 high intensity background
//
// 注意: windows 下 high intensity black + dim 会导致文字不显示 ( high intensity black 和 dim 都是灰色 )
// SEE: https://github.com/chalk/chalk/issues/58
// 其它
// 39 => default color;
// 49 => default background;
// 0 => reset
var NAMES = ['black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white']
var ansiStack = []
var reset = function() { ansiStack.length = 0; return RESET }
var colorMatcher = {
match: /%c/,
handle: function(color) {
var ansi = parseColor(color)
var i = ansi.indexOf(RESET)
if (i >= 0) {
reset()
ansi = ansi.substr(i)
}
ansiStack.push(ansi)
return ansi
},
onEnd: function() {
return clog.autoResetAtEnd ? reset() : ''
},
onGroupEnd: function() {
return clog.autoResetAtGroupEnd ? reset() : ''
},
onFormatEnd: function(arg) {
if (this !== arg.matcher) {
// format 的 value 自带颜色,需要保留它自带的所有样式
if (arg.values.some(hasAnsi)) {
return RESET + ansiStack.join('')
}
}
}
}
// 1. 为了生成 jsdoc 才这样写的
// 2. 下面的那些变量需要先定义,否则 require('clog') 之后 autoResetAtEnd 这些值没没有值
exports = clog
/**
* {@link module:libs/sys/clog} 使用的 format 函数,类似于 console.log 使用了 util.format 函数
* @type {Function}
*/
exports.format = require('./extendFormat')(colorMatcher)
/**
* 是否在 format 的最后加上 ANSI 的 RESET 控制字符
*
* @default true
* @type {Boolean}
*/
exports.autoResetAtEnd = true
/**
* 是否在每一个模板结尾加上 ANSI 的 RESET 控制字符(默认为 true)
*
* 这里解释一下,拿语句 `console.log('Are %s ok', 'you', 'I %s %s', 'am', 'ok')` 来说,
* 句子中共有两个 group(把每一个模板和它的参数叫做一个 group):
* - group 1 => 模板:'Are %s ok', 参数:'you'
* - group 2 => 模板:'I %s %s', 参数:'am', 'ok'
*
* 所以,如果 autoResetAtGroupEnd 是 true,则在上面每个 group 之后都会加上 reset 控制串
*
* @default true
* @type {Boolean}
*/
exports.autoResetAtGroupEnd = true
/**
* 所有支持的具名颜色值,暴露给调用方,调用方可以对其进行修改或替换
*
* 默认支持:
*
* ```
* brown: 'A52A2A',
* chocolate: 'D2691E',
* ghostwhite: 'F8F8FF',
* gold: 'FFD700',
* navy: '000080',
* olive: '808000',
* orange: 'FFA500',
* orangered: 'FF4500',
* pink: 'FFC0CB',
* purple: '800080',
* seagreen: '2E8B57',
* silver: 'C0C0C0',
* skyblue: '87CEEB',
* yellowgreen: '9ACD32'
* ```
*
* @type {Object}
*/
exports.NAMED_COLORS = {
brown: 'A52A2A',
chocolate: 'D2691E',
ghostwhite: 'F8F8FF',
gold: 'FFD700',
navy: '000080',
olive: '808000',
orange: 'FFA500',
orangered: 'FF4500',
pink: 'FFC0CB',
purple: '800080',
seagreen: '2E8B57',
silver: 'C0C0C0',
skyblue: '87CEEB',
yellowgreen: '9ACD32'
}
exports.colorMatcher = colorMatcher // 提供给 xlog.js 使用
/**
* 输出带颜色的格式化字符串
*
* 函数参数和 util.format 类似,只是 util.format 支持 %s %d %j %% 四个格式,
* 此函数多支持一个 %c 的格式,用来指定后面文字的颜色
*
*
* 支持的颜色字符有(所有 CODE 大小写都不敏感):
*
* CODE | EXPLAIN
* -------------------------|---------------
* reset, end | 重置所有色值
* h, high, l, low | 标记颜色的明亮度,只针对 8 种基本颜色有效 {@link https://en.wikipedia.org/wiki/ANSI_escape_code#Colors}
* fg, foreground, c, color | 标记为前景色模式
* bg, background | 标记为背景色模式
* default | 恢复默认的前景色或背景色
* black, red, green, yellow, blue, magenta, cyan, white | 8 种基本颜色(不带标记默认为前景色)
* bgBlack, backgroundBlack, bgRed, backgroundReg, ... | 8 种基本的背景色
* bold, faint, gray, dim, italic, underline, reverse | 一些 modifiers
* rBold, resetBold, rFaint, resetFaint, ... | 重置 modifiers
* brown, chocolate, ghostwhite, gold, navy, ... | 默认的一些颜色,参见 {@link module:libs/sys/clog.NAMED_COLORS}
* F00, FF0000, #F00, #FF0000 ... | 十六进制颜色,支持三位,或六位,支持不带 "#"
*
* @example
* // 下面两个输出的 "you" 是红字黄底,其它文字都是默认的颜色
* clog('Are %cyou%c ok', 'fg.red.bg.yellow', 'reset')
* clog('Are %s ok', clog.format('%cyou', 'red.bgYellow'))
*
* @param {String} template 模板字符串,里面可以包含 %s, %d, %j, %c, %% 这些特殊格式
* @param {...*} [arg] 给模板用的参数
* @param {String} [template2] 第二个模板,后面可以继续接 ...arg, template, ...arg, ...
* @return {String} 格式化后的字符串
*
* @method
* @author Zhonglei Qiu
* @since 2.0.0
*/
module.exports = clog
function clog() {
console.log(exports.format.apply(null, arguments))
}
function hasAnsi(str) {
return typeof str === 'string' && hasAnsiColorRegExp.test(str)
}
// c. 或者 color. 表示设置前景色
// b. bg. 或者 background. 表示设置背景色
// h. l. 或者 high. low. 可以设置成使用 high intensity 相关的颜色
// 每次切换 color 或者 background 都会自动将 high 设置成 false (high intensity color 兼容性不好)
// 而 MODIFIERS 可以随便加,不加前缀时默认使用 color.
function parseColor(color) {
color = String(color)
var bg = false
var high = false
var getNamedColorValue = function(key, forceBG) {
return (high ? 60 : 0) + (bg || forceBG ? 40 : 30) + NAMES.indexOf(key)
}
/* eslint-disable no-multi-spaces, brace-style */
return color
.split(/[{}#.,:;"'\s]+/)
.map(function(raw) {
var k = raw.toLowerCase()
// 空字符串
if (!k) return
// 重置
else if (k === 'reset' || k === 'end') return 0
// 修改状态
else if (k === 'h' || k === 'high') { high = true }
else if (k === 'l' || k === 'low') { high = false }
else if (k === 'fg' || k === 'foreground' || k === 'c' || k === 'color') { high = false; bg = false }
else if (k === 'bg' || k === 'background') { high = false; bg = true }
// 修改颜色
else if (k in MODIFIERS) return MODIFIERS[k]
else if (k === 'default') return bg ? 49 : 39
else if (NAMES.indexOf(k) >= 0) return getNamedColorValue(k)
else if (resetModifRE.test(k) && RegExp.$1 in MODIFIERS) return 20 + MODIFIERS[RegExp.$1] // reset modifier 兼容性不好,少用
else if (bgRE.test(k) && NAMES.indexOf(RegExp.$1) >= 0) return getNamedColorValue(RegExp.$1, true)
// hex 颜色
else if (k in clog.NAMED_COLORS) return getHexColor(clog.NAMED_COLORS[k], bg)
else if (hexRE.test(k)) return getHexColor(k, bg)
// 其它
else return raw // 用户可以自己直接写 ASCII 编码
})
.map(function(n) {
return n == null || n === '' ? '' : PREFIX + n + SUFFIX
})
.join('')
/* eslint-enable no-multi-spaces, brace-style */
}
function getHexColor(hex, bg) {
return (bg ? '48;5;' : '38;5;') + hexToRGB5(hex)
}
function hexToRGB5(hex) {
var rgb, gap
gap = hex.length === 3 ? 1 : 2
rgb = [
hex.substring(0, gap),
hex.substring(gap, gap * 2),
hex.substring(gap * 2, gap * 3)
].map(mapHexToInt5)
return 16 + rgb[0] * 36 + rgb[1] * 6 + rgb[2]
}
function mapHexToInt5(hex) {
return hexToInt5(hex.length === 1 ? hex + hex : hex)
}
function hexToInt5(hex) {
return Math.round((parseInt(hex, 16) || 0) * 5 / 255)
}
|