--- /dev/null
+/*
+ * grunt
+ * http://gruntjs.com/
+ *
+ * Copyright (c) 2014 "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');
+// The upcoming lodash 2.5+ should remove the need for underscore.string.
+var _ = require('lodash');
+_.str = require('underscore.string');
+_.mixin(_.str.exports());
+// 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) {
+ _.bindAll(this);
+ _.bindAll(this.verbose);
+ _.bindAll(this.notverbose);
+ }
+}
+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];
+});