123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299 |
- /*
- * grunt
- * http://gruntjs.com/
- *
- * Copyright (c) 2018 "Cowboy" Ben Alman
- * Licensed under the MIT license.
- * https://github.com/gruntjs/grunt/blob/master/LICENSE-MIT
- */
- 'use strict';
- // Nodejs libs.
- var util = require('util');
- // External libs.
- var hooker = require('hooker');
- // Requiring this here modifies the String prototype!
- var colors = require('colors');
- var _ = require('lodash');
- // TODO: ADD CHALK
- var logUtils = require('grunt-legacy-log-utils');
- function Log(options) {
- // This property always refers to the "base" logger.
- this.always = this;
- // Extend options.
- this.options = _.extend({}, {
- // Show colors in output?
- color: true,
- // Enable verbose-mode logging?
- verbose: false,
- // Enable debug logging statement?
- debug: false,
- // Where should messages be output?
- outStream: process.stdout,
- // NOTE: the color, verbose, debug options will be ignored if the
- // "grunt" option is specified! See the Log.prototype.option and
- // the Log.prototype.error methods for more info.
- grunt: null,
- // Where should output wrap? If null, use legacy Grunt defaults.
- maxCols: null,
- // Should logger start muted?
- muted: false,
- }, options);
- // True once anything has actually been logged.
- this.hasLogged = false;
- // Related verbose / notverbose loggers.
- this.verbose = new VerboseLog(this, true);
- this.notverbose = new VerboseLog(this, false);
- this.verbose.or = this.notverbose;
- this.notverbose.or = this.verbose;
- // Apparently, people have using grunt.log in interesting ways. Just bind
- // all methods so that "this" is irrelevant.
- if (this.options.grunt) {
- var properties = [
- 'write',
- 'writeln',
- 'writetableln',
- 'writelns',
- 'writeflags',
- 'warn',
- 'error',
- 'ok',
- 'errorlns',
- 'oklns',
- 'success',
- 'fail',
- 'header',
- 'subhead',
- 'debug'
- ];
- _.bindAll(this, properties);
- _.bindAll(this.verbose, properties);
- _.bindAll(this.notverbose, properties);
- }
- }
- exports.Log = Log;
- // Am I doing it wrong? :P
- function VerboseLog(parentLog, verbose) {
- // Keep track of the original, base "Log" instance.
- this.always = parentLog;
- // This logger is either verbose (true) or notverbose (false).
- this._isVerbose = verbose;
- }
- util.inherits(VerboseLog, Log);
- VerboseLog.prototype._write = function() {
- // Abort if not in correct verbose mode.
- if (Boolean(this.option('verbose')) !== this._isVerbose) { return; }
- // Otherwise... log!
- return VerboseLog.super_.prototype._write.apply(this, arguments);
- };
- // Create read/write accessors that prefer the parent log's properties (in
- // the case of verbose/notverbose) to the current log's properties.
- function makeSmartAccessor(name, isOption) {
- Object.defineProperty(Log.prototype, name, {
- enumerable: true,
- configurable: true,
- get: function() {
- return isOption ? this.always._options[name] : this.always['_' + name];
- },
- set: function(value) {
- if (isOption) {
- this.always._options[name] = value;
- } else {
- this.always['_' + name] = value;
- }
- },
- });
- }
- makeSmartAccessor('options');
- makeSmartAccessor('hasLogged');
- makeSmartAccessor('muted', true);
- // Disable colors if --no-colors was passed.
- Log.prototype.initColors = function() {
- if (this.option('no-color')) {
- // String color getters should just return the string.
- colors.mode = 'none';
- // Strip colors from strings passed to console.log.
- hooker.hook(console, 'log', function() {
- var args = _.toArray(arguments);
- return hooker.filter(this, args.map(function(arg) {
- return typeof arg === 'string' ? colors.stripColors(arg) : arg;
- }));
- });
- }
- };
- // Check for color, verbose, debug options through Grunt if specified,
- // otherwise defer to options object properties.
- Log.prototype.option = function(name) {
- if (this.options.grunt && this.options.grunt.option) {
- return this.options.grunt.option(name);
- }
- var no = name.match(/^no-(.+)$/);
- return no ? !this.options[no[1]] : this.options[name];
- };
- // Parse certain markup in strings to be logged.
- Log.prototype._markup = function(str) {
- str = str || '';
- // Make _foo_ underline.
- str = str.replace(/(\s|^)_(\S|\S[\s\S]+?\S)_(?=[\s,.!?]|$)/g, '$1' + '$2'.underline);
- // Make *foo* bold.
- str = str.replace(/(\s|^)\*(\S|\S[\s\S]+?\S)\*(?=[\s,.!?]|$)/g, '$1' + '$2'.bold);
- return str;
- };
- // Similar to util.format in the standard library, however it'll always
- // convert the first argument to a string and treat it as the format string.
- Log.prototype._format = function(args) {
- args = _.toArray(args);
- if (args.length > 0) {
- args[0] = String(args[0]);
- }
- return util.format.apply(util, args);
- };
- Log.prototype._write = function(msg) {
- // Abort if muted.
- if (this.muted) { return; }
- // Actually write output.
- this.hasLogged = true;
- msg = msg || '';
- // Users should probably use the colors-provided methods, but if they
- // don't, this should strip extraneous color codes.
- if (this.option('no-color')) { msg = colors.stripColors(msg); }
- // Actually write to stdout.
- this.options.outStream.write(this._markup(msg));
- };
- Log.prototype._writeln = function(msg) {
- // Write blank line if no msg is passed in.
- this._write((msg || '') + '\n');
- };
- // Write output.
- Log.prototype.write = function() {
- this._write(this._format(arguments));
- return this;
- };
- // Write a line of output.
- Log.prototype.writeln = function() {
- this._writeln(this._format(arguments));
- return this;
- };
- Log.prototype.warn = function() {
- var msg = this._format(arguments);
- if (arguments.length > 0) {
- this._writeln('>> '.red + _.trim(msg).replace(/\n/g, '\n>> '.red));
- } else {
- this._writeln('ERROR'.red);
- }
- return this;
- };
- Log.prototype.error = function() {
- if (this.options.grunt && this.options.grunt.fail) {
- this.options.grunt.fail.errorcount++;
- }
- this.warn.apply(this, arguments);
- return this;
- };
- Log.prototype.ok = function() {
- var msg = this._format(arguments);
- if (arguments.length > 0) {
- this._writeln('>> '.green + _.trim(msg).replace(/\n/g, '\n>> '.green));
- } else {
- this._writeln('OK'.green);
- }
- return this;
- };
- Log.prototype.errorlns = function() {
- var msg = this._format(arguments);
- this.error(this.wraptext(this.options.maxCols || 77, msg));
- return this;
- };
- Log.prototype.oklns = function() {
- var msg = this._format(arguments);
- this.ok(this.wraptext(this.options.maxCols || 77, msg));
- return this;
- };
- Log.prototype.success = function() {
- var msg = this._format(arguments);
- this._writeln(msg.green);
- return this;
- };
- Log.prototype.fail = function() {
- var msg = this._format(arguments);
- this._writeln(msg.red);
- return this;
- };
- Log.prototype.header = function() {
- var msg = this._format(arguments);
- // Skip line before header, but not if header is the very first line output.
- if (this.hasLogged) { this._writeln(); }
- this._writeln(msg.underline);
- return this;
- };
- Log.prototype.subhead = function() {
- var msg = this._format(arguments);
- // Skip line before subhead, but not if subhead is the very first line output.
- if (this.hasLogged) { this._writeln(); }
- this._writeln(msg.bold);
- return this;
- };
- // For debugging.
- Log.prototype.debug = function() {
- var msg = this._format(arguments);
- if (this.option('debug')) {
- this._writeln('[D] ' + msg.magenta);
- }
- return this;
- };
- // Write a line of a table.
- Log.prototype.writetableln = function(widths, texts) {
- this._writeln(this.table(widths, texts));
- return this;
- };
- // Wrap a long line of text.
- Log.prototype.writelns = function() {
- var msg = this._format(arguments);
- this._writeln(this.wraptext(this.options.maxCols || 80, msg));
- return this;
- };
- // Display flags in verbose mode.
- Log.prototype.writeflags = function(obj, prefix) {
- var wordlist;
- if (Array.isArray(obj)) {
- wordlist = this.wordlist(obj);
- } else if (typeof obj === 'object' && obj) {
- wordlist = this.wordlist(Object.keys(obj).map(function(key) {
- var val = obj[key];
- return key + (val === true ? '' : '=' + JSON.stringify(val));
- }));
- }
- this._writeln((prefix || 'Flags') + ': ' + (wordlist || '(none)'.cyan));
- return this;
- };
- // Add static methods.
- [
- 'wordlist',
- 'uncolor',
- 'wraptext',
- 'table',
- ].forEach(function(prop) {
- Log.prototype[prop] = exports[prop] = logUtils[prop];
- });
|