Version 1
[yaffs-website] / node_modules / grunt-legacy-log / index.js
1 /*
2  * grunt
3  * http://gruntjs.com/
4  *
5  * Copyright (c) 2014 "Cowboy" Ben Alman
6  * Licensed under the MIT license.
7  * https://github.com/gruntjs/grunt/blob/master/LICENSE-MIT
8  */
9
10 'use strict';
11
12 // Nodejs libs.
13 var util = require('util');
14
15 // External libs.
16 var hooker = require('hooker');
17 // Requiring this here modifies the String prototype!
18 var colors = require('colors');
19 // The upcoming lodash 2.5+ should remove the need for underscore.string.
20 var _ = require('lodash');
21 _.str = require('underscore.string');
22 _.mixin(_.str.exports());
23 // TODO: ADD CHALK
24
25 var logUtils = require('grunt-legacy-log-utils');
26
27 function Log(options) {
28   // This property always refers to the "base" logger.
29   this.always = this;
30   // Extend options.
31   this.options = _.extend({}, {
32     // Show colors in output?
33     color: true,
34     // Enable verbose-mode logging?
35     verbose: false,
36     // Enable debug logging statement?
37     debug: false,
38     // Where should messages be output?
39     outStream: process.stdout,
40     // NOTE: the color, verbose, debug options will be ignored if the
41     // "grunt" option is specified! See the Log.prototype.option and
42     // the Log.prototype.error methods for more info.
43     grunt: null,
44     // Where should output wrap? If null, use legacy Grunt defaults.
45     maxCols: null,
46     // Should logger start muted?
47     muted: false,
48   }, options);
49   // True once anything has actually been logged.
50   this.hasLogged = false;
51
52   // Related verbose / notverbose loggers.
53   this.verbose = new VerboseLog(this, true);
54   this.notverbose = new VerboseLog(this, false);
55   this.verbose.or = this.notverbose;
56   this.notverbose.or = this.verbose;
57
58   // Apparently, people have using grunt.log in interesting ways. Just bind
59   // all methods so that "this" is irrelevant.
60   if (this.options.grunt) {
61     _.bindAll(this);
62     _.bindAll(this.verbose);
63     _.bindAll(this.notverbose);
64   }
65 }
66 exports.Log = Log;
67
68 // Am I doing it wrong? :P
69 function VerboseLog(parentLog, verbose) {
70   // Keep track of the original, base "Log" instance.
71   this.always = parentLog;
72   // This logger is either verbose (true) or notverbose (false).
73   this._isVerbose = verbose;
74 }
75 util.inherits(VerboseLog, Log);
76
77 VerboseLog.prototype._write = function() {
78   // Abort if not in correct verbose mode.
79   if (Boolean(this.option('verbose')) !== this._isVerbose) { return; }
80   // Otherwise... log!
81   return VerboseLog.super_.prototype._write.apply(this, arguments);
82 };
83
84 // Create read/write accessors that prefer the parent log's properties (in
85 // the case of verbose/notverbose) to the current log's properties.
86 function makeSmartAccessor(name, isOption) {
87   Object.defineProperty(Log.prototype, name, {
88     enumerable: true,
89     configurable: true,
90     get: function() {
91       return isOption ? this.always._options[name] : this.always['_' + name];
92     },
93     set: function(value) {
94       if (isOption) {
95         this.always._options[name] = value;
96       } else {
97         this.always['_' + name] = value;
98       }
99     },
100   });
101 }
102 makeSmartAccessor('options');
103 makeSmartAccessor('hasLogged');
104 makeSmartAccessor('muted', true);
105
106 // Disable colors if --no-colors was passed.
107 Log.prototype.initColors = function() {
108   if (this.option('no-color')) {
109     // String color getters should just return the string.
110     colors.mode = 'none';
111     // Strip colors from strings passed to console.log.
112     hooker.hook(console, 'log', function() {
113       var args = _.toArray(arguments);
114       return hooker.filter(this, args.map(function(arg) {
115         return typeof arg === 'string' ? colors.stripColors(arg) : arg;
116       }));
117     });
118   }
119 };
120
121 // Check for color, verbose, debug options through Grunt if specified,
122 // otherwise defer to options object properties.
123 Log.prototype.option = function(name) {
124   if (this.options.grunt && this.options.grunt.option) {
125     return this.options.grunt.option(name);
126   }
127   var no = name.match(/^no-(.+)$/);
128   return no ? !this.options[no[1]] : this.options[name];
129 };
130
131 // Parse certain markup in strings to be logged.
132 Log.prototype._markup = function(str) {
133   str = str || '';
134   // Make _foo_ underline.
135   str = str.replace(/(\s|^)_(\S|\S[\s\S]+?\S)_(?=[\s,.!?]|$)/g, '$1' + '$2'.underline);
136   // Make *foo* bold.
137   str = str.replace(/(\s|^)\*(\S|\S[\s\S]+?\S)\*(?=[\s,.!?]|$)/g, '$1' + '$2'.bold);
138   return str;
139 };
140
141 // Similar to util.format in the standard library, however it'll always
142 // convert the first argument to a string and treat it as the format string.
143 Log.prototype._format = function(args) {
144   args = _.toArray(args);
145   if (args.length > 0) {
146     args[0] = String(args[0]);
147   }
148   return util.format.apply(util, args);
149 };
150
151 Log.prototype._write = function(msg) {
152   // Abort if muted.
153   if (this.muted) { return; }
154   // Actually write output.
155   this.hasLogged = true;
156   msg = msg || '';
157   // Users should probably use the colors-provided methods, but if they
158   // don't, this should strip extraneous color codes.
159   if (this.option('no-color')) { msg = colors.stripColors(msg); }
160   // Actually write to stdout.
161   this.options.outStream.write(this._markup(msg));
162 };
163
164 Log.prototype._writeln = function(msg) {
165   // Write blank line if no msg is passed in.
166   this._write((msg || '') + '\n');
167 };
168
169 // Write output.
170 Log.prototype.write = function() {
171   this._write(this._format(arguments));
172   return this;
173 };
174
175 // Write a line of output.
176 Log.prototype.writeln = function() {
177   this._writeln(this._format(arguments));
178   return this;
179 };
180
181 Log.prototype.warn = function() {
182   var msg = this._format(arguments);
183   if (arguments.length > 0) {
184     this._writeln('>> '.red + _.trim(msg).replace(/\n/g, '\n>> '.red));
185   } else {
186     this._writeln('ERROR'.red);
187   }
188   return this;
189 };
190 Log.prototype.error = function() {
191   if (this.options.grunt && this.options.grunt.fail) {
192     this.options.grunt.fail.errorcount++;
193   }
194   this.warn.apply(this, arguments);
195   return this;
196 };
197 Log.prototype.ok = function() {
198   var msg = this._format(arguments);
199   if (arguments.length > 0) {
200     this._writeln('>> '.green + _.trim(msg).replace(/\n/g, '\n>> '.green));
201   } else {
202     this._writeln('OK'.green);
203   }
204   return this;
205 };
206 Log.prototype.errorlns = function() {
207   var msg = this._format(arguments);
208   this.error(this.wraptext(this.options.maxCols || 77, msg));
209   return this;
210 };
211 Log.prototype.oklns = function() {
212   var msg = this._format(arguments);
213   this.ok(this.wraptext(this.options.maxCols || 77, msg));
214   return this;
215 };
216 Log.prototype.success = function() {
217   var msg = this._format(arguments);
218   this._writeln(msg.green);
219   return this;
220 };
221 Log.prototype.fail = function() {
222   var msg = this._format(arguments);
223   this._writeln(msg.red);
224   return this;
225 };
226 Log.prototype.header = function() {
227   var msg = this._format(arguments);
228   // Skip line before header, but not if header is the very first line output.
229   if (this.hasLogged) { this._writeln(); }
230   this._writeln(msg.underline);
231   return this;
232 };
233 Log.prototype.subhead = function() {
234   var msg = this._format(arguments);
235   // Skip line before subhead, but not if subhead is the very first line output.
236   if (this.hasLogged) { this._writeln(); }
237   this._writeln(msg.bold);
238   return this;
239 };
240 // For debugging.
241 Log.prototype.debug = function() {
242   var msg = this._format(arguments);
243   if (this.option('debug')) {
244     this._writeln('[D] ' + msg.magenta);
245   }
246   return this;
247 };
248
249 // Write a line of a table.
250 Log.prototype.writetableln = function(widths, texts) {
251   this._writeln(this.table(widths, texts));
252   return this;
253 };
254
255 // Wrap a long line of text.
256 Log.prototype.writelns = function() {
257   var msg = this._format(arguments);
258   this._writeln(this.wraptext(this.options.maxCols || 80, msg));
259   return this;
260 };
261
262 // Display flags in verbose mode.
263 Log.prototype.writeflags = function(obj, prefix) {
264   var wordlist;
265   if (Array.isArray(obj)) {
266     wordlist = this.wordlist(obj);
267   } else if (typeof obj === 'object' && obj) {
268     wordlist = this.wordlist(Object.keys(obj).map(function(key) {
269       var val = obj[key];
270       return key + (val === true ? '' : '=' + JSON.stringify(val));
271     }));
272   }
273   this._writeln((prefix || 'Flags') + ': ' + (wordlist || '(none)'.cyan));
274   return this;
275 };
276
277 // Add static methods.
278 [
279   'wordlist',
280   'uncolor',
281   'wraptext',
282   'table',
283 ].forEach(function(prop) {
284   Log.prototype[prop] = exports[prop] = logUtils[prop];
285 });