UNPKG

16.1 kBJavaScriptView Raw
1'use strict';
2
3Object.defineProperty(exports, '__esModule', { value: true });
4
5function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
6
7var path = require('path');
8var fs = require('fs');
9var dayjs = _interopDefault(require('dayjs'));
10var util = _interopDefault(require('util'));
11var chalk = _interopDefault(require('chalk'));
12var stringWidth = _interopDefault(require('string-width'));
13var figures = _interopDefault(require('figures'));
14
15var Types = {
16 // Level 0
17 fatal: {
18 level: 0
19 },
20 error: {
21 level: 0
22 },
23 // Level 1
24 warn: {
25 level: 1
26 },
27 // Level 2
28 log: {
29 level: 2
30 },
31 // Level 3
32 info: {
33 level: 3
34 },
35 success: {
36 level: 3
37 },
38 // Level 4
39 debug: {
40 level: 4
41 },
42 // Level 5
43 trace: {
44 level: 5
45 },
46 // Silent
47 silent: {
48 level: Infinity
49 },
50 // Legacy
51 ready: {
52 level: 3
53 },
54 start: {
55 level: 3
56 }
57};
58
59function isPlainObject(obj) {
60 return Object.prototype.toString.call(obj) === '[object Object]';
61}
62function isLogObj(arg) {
63 // Should be plain object
64 // Also contains either message or args field
65 return isPlainObject(arg) && Boolean(arg.message || arg.args);
66}
67
68let paused = false;
69const queue = [];
70class Consola {
71 constructor(options = {}) {
72 this._reporters = options.reporters || [];
73 this._types = options.types || Types;
74 this._level = options.level != null ? options.level : 3;
75 this._defaults = options.defaults || {};
76 this._async = typeof options.async !== 'undefined' ? options.async : null;
77 this._stdout = options.stdout;
78 this._stderr = options.stdout;
79 this._mockFn = options.mockFn; // Create logger functions for current instance
80
81 for (const type in this._types) {
82 this[type] = this._wrapLogFn(Object.assign({
83 type
84 }, this._types[type], this._defaults));
85 } // Use _mockFn if is set
86
87
88 if (this._mockFn) {
89 this.mockTypes();
90 }
91 }
92
93 get level() {
94 return this._level;
95 }
96
97 set level(newLevel) {
98 // Ensure that newLevel does not exceeds type level boundaries
99 let min = 0;
100 let max = 0;
101
102 for (const typeName in this._types) {
103 const type = this._types[typeName];
104
105 if (type.level > max) {
106 max = type.level;
107 } else if (type.level < min) {
108 min = type.level;
109 }
110 } // Set level
111
112
113 this._level = Math.min(max, Math.max(min, newLevel));
114 }
115
116 get stdout() {
117 return this._stdout || console._stdout; // eslint-disable-line no-console
118 }
119
120 get stderr() {
121 return this._stderr || console._stderr; // eslint-disable-line no-console
122 }
123
124 create(options) {
125 return new Consola(Object.assign({
126 reporters: this._reporters,
127 level: this._level,
128 types: this._types,
129 defaults: this._defaults,
130 stdout: this._stdout,
131 stderr: this._stderr,
132 mockFn: this._mockFn
133 }, options));
134 }
135
136 addReporter(reporter) {
137 this._reporters.push(reporter);
138
139 return this;
140 }
141
142 removeReporter(reporter) {
143 if (reporter) {
144 const i = this._reporters.indexOf(reporter);
145
146 if (i >= 0) {
147 return this._reporters.splice(i, 1);
148 }
149 } else {
150 this._reporters.splice(0);
151 }
152
153 return this;
154 }
155
156 setReporters(reporters) {
157 this._reporters = Array.isArray(reporters) ? reporters : [reporters];
158 }
159
160 withDefaults(defaults) {
161 return this.create({
162 defaults: Object.assign({}, this._defaults, defaults)
163 });
164 }
165
166 withTag(tag) {
167 return this.withDefaults({
168 tag: this._defaults.tag ? this._defaults.tag + ':' + tag : tag
169 });
170 }
171
172 wrapAll() {
173 this.wrapConsole();
174 this.wrapStd();
175 }
176
177 restoreAll() {
178 this.restoreConsole();
179 this.restoreStd();
180 }
181
182 wrapConsole() {
183 for (const type in this._types) {
184 // Backup original value
185 if (!console['__' + type]) {
186 // eslint-disable-line no-console
187 console['__' + type] = console[type]; // eslint-disable-line no-console
188 } // Override
189
190
191 console[type] = this[type]; // eslint-disable-line no-console
192 }
193 }
194
195 restoreConsole() {
196 for (const type in this._types) {
197 // Restore if backup is available
198 if (console['__' + type]) {
199 // eslint-disable-line no-console
200 console[type] = console['__' + type]; // eslint-disable-line no-console
201
202 delete console['__' + type]; // eslint-disable-line no-console
203 }
204 }
205 }
206
207 wrapStd() {
208 this._wrapStream(this.stdout, 'log');
209
210 this._wrapStream(this.stderr, 'log');
211 }
212
213 _wrapStream(stream, type) {
214 if (!stream) {
215 return;
216 } // Backup original value
217
218
219 if (!stream.__write) {
220 stream.__write = stream.write;
221 } // Override
222
223
224 stream.write = data => {
225 this[type](String(data).trim());
226 };
227 }
228
229 restoreStd() {
230 this._restoreStream(this.stdout);
231
232 this._restoreStream(this.stderr);
233 }
234
235 _restoreStream(stream) {
236 if (!stream) {
237 return;
238 }
239
240 if (stream.__write) {
241 stream.write = stream.__write;
242 delete stream.__write;
243 }
244 }
245
246 pauseLogs() {
247 paused = true;
248 }
249
250 resumeLogs() {
251 paused = false; // Process queue
252
253 const _queue = queue.splice(0);
254
255 for (const item of _queue) {
256 item[0]._logFn(item[1], item[2]);
257 }
258 }
259
260 mockTypes(mockFn) {
261 this._mockFn = mockFn || this._mockFn;
262
263 if (typeof this._mockFn !== 'function') {
264 return;
265 }
266
267 for (const type in this._types) {
268 this[type] = this._mockFn(type, this._types[type]) || this[type];
269 }
270 }
271
272 _wrapLogFn(defaults) {
273 function logFn() {
274 if (paused) {
275 queue.push([this, defaults, arguments]);
276 return;
277 }
278
279 return this._logFn(defaults, arguments);
280 }
281
282 return logFn.bind(this);
283 }
284
285 _logFn(defaults, args) {
286 if (defaults.level > this._level) {
287 return this._async ? Promise.resolve(false) : false;
288 } // Construct a new log object
289
290
291 const logObj = Object.assign({
292 date: new Date(),
293 args: []
294 }, defaults); // Consume arguments
295
296 if (args.length === 1 && isLogObj(args[0])) {
297 Object.assign(logObj, args[0]);
298 } else {
299 logObj.args = Array.from(args);
300 } // Aliases
301
302
303 if (logObj.message) {
304 logObj.args.unshift(logObj.message);
305 delete logObj.message;
306 }
307
308 if (logObj.additional) {
309 if (!Array.isArray(logObj.additional)) {
310 logObj.additional = logObj.additional.split('\n');
311 }
312
313 logObj.args.push('\n' + logObj.additional.join('\n'));
314 delete logObj.additional;
315 } // Log
316
317
318 if (this._async) {
319 return this._logAsync(logObj);
320 } else {
321 this._log(logObj);
322 }
323 }
324
325 _log(logObj) {
326 for (const reporter of this._reporters) {
327 reporter.log(logObj, {
328 async: false,
329 stdout: this.stdout,
330 stderr: this.stderr
331 });
332 }
333 }
334
335 _logAsync(logObj) {
336 return Promise.all(this._reporters.map(reporter => reporter.log(logObj, {
337 async: true,
338 stdout: this.stdout,
339 stderr: this.stderr
340 })));
341 }
342
343} // Legacy support
344
345Consola.prototype.add = Consola.prototype.addReporter;
346Consola.prototype.remove = Consola.prototype.removeReporter;
347Consola.prototype.clear = Consola.prototype.removeReporter;
348Consola.prototype.withScope = Consola.prototype.withTag;
349Consola.prototype.mock = Consola.prototype.mockTypes;
350Consola.prototype.pause = Consola.prototype.pauseLogs;
351Consola.prototype.resume = Consola.prototype.resumeLogs;
352
353function assignGlobalReference(newInstance, referenceKey) {
354 if (!newInstance.constructor || global[referenceKey] && !global[referenceKey].constructor) {
355 throw new Error('Assigning to global reference is only supported for class instances');
356 } else if (newInstance.constructor && !global[referenceKey]) {
357 global[referenceKey] = newInstance;
358 } else if (!(newInstance instanceof global[referenceKey].constructor || global[referenceKey] instanceof newInstance.constructor)) {
359 throw new Error(`Not a ${global[referenceKey].constructor.name} instance`);
360 }
361
362 const oldInstance = Object.create(global[referenceKey]);
363
364 for (let prop in global[referenceKey]) {
365 oldInstance[prop] = global[referenceKey][prop];
366 delete global[referenceKey][prop];
367 }
368
369 for (let prop of Object.getOwnPropertySymbols(global[referenceKey])) {
370 oldInstance[prop] = global[referenceKey][prop];
371 delete global[referenceKey][prop];
372 }
373
374 for (let prop in newInstance) {
375 global[referenceKey][prop] = newInstance[prop];
376 }
377
378 for (let prop of Object.getOwnPropertySymbols(newInstance)) {
379 global[referenceKey][prop] = newInstance[prop];
380 }
381
382 return oldInstance;
383}
384function assignGlobalConsola(newConsola) {
385 return assignGlobalReference(newConsola, 'consola');
386}
387
388function parseStack(stack) {
389 const cwd = process.cwd() + path.sep;
390 let lines = stack.split('\n').splice(1).map(l => l.trim().replace('file://', '').replace(cwd, ''));
391 return lines;
392}
393
394function writeStream(data, stream, mode = 'default') {
395 const write = stream.__write || stream.write;
396
397 switch (mode) {
398 case 'async':
399 return new Promise(resolve => {
400 if (write.call(stream, data) === true) {
401 resolve();
402 } else {
403 stream.once('drain', () => {
404 resolve();
405 });
406 }
407 });
408
409 case 'sync':
410 return fs.writeSync(stream.fd, data);
411
412 default:
413 return write.call(stream, data);
414 }
415}
416
417function formatDate(timeFormat, date) {
418 return dayjs(date).format(timeFormat);
419}
420
421const DEFAULTS = {
422 dateFormat: 'HH:mm:ss',
423 formatOptions: {
424 colors: false,
425 compact: true
426 }
427};
428
429const bracket = x => x ? `[${x}]` : '';
430
431class BasicReporter {
432 constructor(options) {
433 this.options = Object.assign({}, DEFAULTS, options);
434 }
435
436 formatStack(stack) {
437 return ' ' + parseStack(stack).join('\n ');
438 }
439
440 formatArgs(args) {
441 const _args = args.map(arg => {
442 if (arg && typeof arg.stack === 'string') {
443 return arg.message + '\n' + this.formatStack(arg.stack);
444 }
445
446 return arg;
447 }); // Only supported with Node >= 10
448 // https://nodejs.org/api/util.html#util_util_inspect_object_options
449
450
451 if (typeof util.formatWithOptions === 'function') {
452 return util.formatWithOptions(this.options.formatOptions, ..._args);
453 } else {
454 return util.format(..._args);
455 }
456 }
457
458 formatDate(date) {
459 return formatDate(this.options.dateFormat, date);
460 }
461
462 filterAndJoin(arr) {
463 return arr.filter(x => x).join(' ');
464 }
465
466 formatLogObj(logObj) {
467 const message = this.formatArgs(logObj.args);
468 const date = this.formatDate(logObj.date);
469 const type = logObj.type.toUpperCase();
470 return this.filterAndJoin([bracket(date), bracket(logObj.tag), bracket(type), message]);
471 }
472
473 log(logObj, {
474 async,
475 stdout,
476 stderr
477 } = {}) {
478 const line = this.formatLogObj(logObj, {
479 width: stdout.columns ? stdout.columns - 1 : 80
480 });
481 return writeStream(line + '\n', logObj.level < 2 ? stderr : stdout, async ? 'async' : 'default');
482 }
483
484}
485
486const TYPE_COLOR_MAP = {
487 'info': 'cyan'
488};
489const LEVEL_COLOR_MAP = {
490 0: 'red',
491 1: 'yellow',
492 2: 'white',
493 3: 'green'
494};
495
496class BrowserReporter {
497 constructor(options) {
498 this.options = Object.assign({}, options);
499 }
500
501 log(logObj) {
502 // consoleLogFn
503 let consoleLogFn = console[logObj.type]; // eslint-disable-line no-console
504
505 if (!consoleLogFn) {
506 consoleLogFn = console[logObj.level < 2 ? 'error' : 'log']; // eslint-disable-line no-console
507 } // Type
508
509
510 const type = logObj.type.toUpperCase(); // Styles
511
512 const color = TYPE_COLOR_MAP[logObj.type] || LEVEL_COLOR_MAP[logObj.level];
513 const styleColor = `color: ${color}; background-color: inherit;`;
514 const styleInherit = `color: inherit; background-color: inherit;`;
515 const styleAdditional = `color: ${logObj.additionalColor || 'grey'}; background-color: inherit;`; // Date
516
517 const date = new Date(logObj.date).toLocaleTimeString(); // Log to the console
518
519 consoleLogFn(`%c[${type}]%c[${date}]%c`, styleColor, styleAdditional, styleInherit, ...logObj.args);
520 }
521
522}
523
524const _colorCache = {};
525function chalkColor(name) {
526 let color = _colorCache[name];
527
528 if (color) {
529 return color;
530 }
531
532 if (name[0] === '#') {
533 color = chalk.hex(name);
534 } else {
535 color = chalk[name] || chalk.keyword(name);
536 }
537
538 _colorCache[name] = color;
539 return color;
540}
541const _bgColorCache = {};
542function chalkBgColor(name) {
543 let color = _bgColorCache[name];
544
545 if (color) {
546 return color;
547 }
548
549 if (name[0] === '#') {
550 color = chalk.bgHex(name);
551 } else {
552 color = chalk['bg' + name[0].toUpperCase() + name.slice(1)] || chalk.bgKeyword(name);
553 }
554
555 _bgColorCache[name] = color;
556 return color;
557}
558
559const DEFAULTS$1 = {
560 secondaryColor: 'grey',
561 formatOptions: {
562 colors: true,
563 compact: false
564 }
565};
566const TYPE_ICONS = {
567 info: figures('ℹ'),
568 success: figures('✔'),
569 debug: figures('›'),
570 trace: figures('›'),
571 log: ''
572};
573class FancyReporter extends BasicReporter {
574 constructor(options) {
575 super(Object.assign({}, DEFAULTS$1, options));
576 }
577
578 formatStack(stack) {
579 const color1 = chalkColor('grey');
580 const color2 = chalkColor('cyan');
581 const color3 = chalkColor('reset');
582 return '\n' + parseStack(stack).map(line => color2(' ' + line.replace(/^at /, m => color1(m)).replace(/\(.*\)/, m => color3(m)))).join('\n');
583 }
584
585 typeColor(type, level) {
586 return chalkColor();
587 }
588
589 formatType(logObj, isBadge) {
590 const typeColor = TYPE_COLOR_MAP[logObj.type] || LEVEL_COLOR_MAP[logObj.level] || this.options.secondaryColor;
591
592 if (isBadge) {
593 return chalkBgColor(typeColor).black(` ${logObj.type.toUpperCase()} `);
594 }
595
596 const _type = typeof TYPE_ICONS[logObj.type] === 'string' ? TYPE_ICONS[logObj.type] : logObj.icon || logObj.type;
597
598 return _type ? chalkColor(typeColor)(_type) : '';
599 }
600
601 formatLogObj(logObj, {
602 width
603 }) {
604 const [message, ...additional] = this.formatArgs(logObj.args).split('\n');
605 const isBadge = typeof logObj.badge !== 'undefined' ? Boolean(logObj.badge) : logObj.level < 2;
606 const secondaryColor = chalkColor(this.options.secondaryColor);
607 const date = secondaryColor(this.formatDate(logObj.date));
608 const type = this.formatType(logObj, isBadge);
609 const tag = logObj.tag ? secondaryColor(logObj.tag) : '';
610 let left = this.filterAndJoin([type, message]);
611 let right = this.filterAndJoin([tag, date]);
612 const space = width - stringWidth(left) - stringWidth(right) - 2;
613 let line = space > 0 ? left + ' '.repeat(space) + right : left;
614 line += additional.length ? '\n' + additional.join('\n') : '';
615 return isBadge ? '\n' + line + '\n' : line;
616 }
617
618}
619
620class JSONReporter {
621 constructor({
622 stream
623 }) {
624 this.stream = stream || process.stdout;
625 }
626
627 log(logObj) {
628 this.stream.write(JSON.stringify(logObj) + '\n');
629 }
630
631}
632
633// This reporter is compatible with Winston 3
634// https://github.com/winstonjs/winston
635class WinstonReporter {
636 constructor(logger) {
637 if (logger && logger.log) {
638 this.logger = logger;
639 } else {
640 const winston = require('winston');
641
642 this.logger = winston.createLogger(Object.assign({
643 level: 'info',
644 format: winston.format.simple(),
645 transports: [new winston.transports.Console()]
646 }, logger));
647 }
648 }
649
650 log(logObj) {
651 const args = [].concat(logObj.args);
652 const arg0 = args.shift();
653 this.logger.log({
654 level: levels[logObj.level] || 'info',
655 label: logObj.tag,
656 message: arg0,
657 args: args,
658 timestamp: logObj.date.getTime() / 1000
659 });
660 }
661
662}
663const levels = {
664 0: 'error',
665 1: 'warn',
666 2: 'info',
667 3: 'verbose',
668 4: 'debug',
669 5: 'silly'
670};
671
672exports.Consola = Consola;
673exports.Types = Types;
674exports.isLogObj = isLogObj;
675exports.assignGlobalConsola = assignGlobalConsola;
676exports.BasicReporter = BasicReporter;
677exports.BrowserReporter = BrowserReporter;
678exports.FancyReporter = FancyReporter;
679exports.JSONReporter = JSONReporter;
680exports.WinstonReporter = WinstonReporter;