UNPKG

grunt

Version:

A task-based command line build tool for JavaScript projects.

260 lines (229 loc) 7.76 kB
/* * grunt * https://github.com/cowboy/grunt * * Copyright (c) 2012 "Cowboy" Ben Alman * Licensed under the MIT license. * http://benalman.com/about/license/ */ var nodeutil = require('util'); // Temporarily suppress output. var suppressOutput; // Allow external muting of output. exports.muted = false; // True once anything has actually been logged. var hasLogged; // Apparently writing to stdout in node.js is non-blocking. But stderr blocks. // So we'll use that instead. WTF. https://github.com/joyent/node/issues/1669 process.stdout = process.stderr; // Write output. exports.write = function(msg) { // Actually write output. if (!exports.muted && !suppressOutput) { hasLogged = true; process.stdout.write(msg || ''); } // Chainable! return this; }; // Write a line of output. exports.writeln = function(msg) { // Actually write output. this.write((msg || '') + '\n'); // Chainable! return this; }; // Stuff. exports.error = function(msg) { if (msg) { fail.errorcount++; return this.writeln('>> '.red + util.chomp(msg).replace(/\n/g, '\n>> '.red)); } else { return this.writeln('ERROR'.red); } }; exports.ok = function(msg) { if (msg) { return this.writeln('>> '.green + util.chomp(msg).replace(/\n/g, '\n>> '.green)); } else { return this.writeln('OK'.green); } }; exports.success = function(msg) { return this.writeln(msg.green); }; exports.fail = function(msg) { return this.writeln(msg.red); }; exports.header = function(msg) { // Skip line before header, but not if header is the very first line output. if (hasLogged) { this.writeln(); } return this.writeln(msg.underline); }; exports.subhead = function(msg) { // Skip line before subhead, but not if subhead is the very first line output. if (hasLogged) { this.writeln(); } return this.writeln(msg.bold); }; // For debugging. exports.debug = function() { if (option('debug')) { this.writeln(('[D] ' + nodeutil.format.apply(this, arguments)).magenta); } return this; }; // Write a line of a table. exports.writetableln = function(widths, texts) { return this.writeln(this.table(widths, texts)); }; // Wrap a long line of text to 80 columns. exports.writelns = function(msg) { return this.writeln(exports.wraptext(80, msg)); }; // Display flags in verbose mode. exports.writeflags = function(obj, prefix) { var wordlist; if (obj instanceof Array) { wordlist = exports.wordlist(obj); } else if (typeof obj === 'object' && obj) { wordlist = exports.wordlist(Object.keys(obj).map(function(key) { var val = obj[key]; return key + (val === true ? '' : '=' + JSON.stringify(val)); })); } return this.writeln((prefix || 'Flags') + ': ' + (wordlist || '(none)'.cyan)); }; // Create explicit "verbose" and "notverbose" functions, one for each already- // defined log function, that do the same thing but ONLY if -v or --verbose is // specified (or not specified). exports.verbose = {}; exports.notverbose = {}; // Iterate over all exported functions. Object.keys(exports).filter(function(key) { return typeof exports[key] === 'function'; }).forEach(function(key) { // Like any other log function, but suppresses output if the "verbose" option // IS NOT set. exports.verbose[key] = function() { suppressOutput = !option('verbose'); exports[key].apply(this, arguments); suppressOutput = false; return this; }; // Like any other log function, but suppresses output if the "verbose" option // IS set. exports.notverbose[key] = function() { suppressOutput = option('verbose'); exports[key].apply(this, arguments); suppressOutput = false; return this; }; }); // A way to switch between verbose and notverbose modes. For example, this will // write 'foo' if verbose logging is enabled, otherwise write 'bar': // verbose.write('foo').or.write('bar'); exports.verbose.or = exports.notverbose; exports.notverbose.or = exports.verbose; // Static methods. // Pretty-format a word list. exports.wordlist = function(arr, separator) { return arr.map(function(item) { return item.cyan; }).join(separator || ', '); }; // Return a string, uncolored (suitable for testing .length, etc). exports.uncolor = function(str) { return str.replace(/\x1B\[\d+m/g, ''); }; // Word-wrap text to a given width, permitting ANSI color codes. exports.wraptext = function(width, text) { // notes to self: // grab 1st character or ansi code from string // if ansi code, add to array and save for later, strip from front of string // if character, add to array and increment counter, strip from front of string // if width + 1 is reached and current character isn't space: // slice off everything after last space in array and prepend it to string // etc // This result array will be joined on \n. var result = []; var matches, color, tmp; var captured = []; var charlen = 0; while (matches = text.match(/(?:(\x1B\[\d+m)|\n|(.))([\s\S]*)/)) { // Updated text to be everything not matched. text = matches[3]; // Matched a color code? if (matches[1]) { // Save last captured color code for later use. color = matches[1]; // Capture color code. captured.push(matches[1]); continue; // Matched a non-newline character? } else if (matches[2]) { // If this is the first character and a previous color code was set, push // that onto the captured array first. if (charlen === 0 && color) { captured.push(color); } // Push the matched character. captured.push(matches[2]); // Increment the current charlen. charlen++; // If not yet at the width limit or a space was matched, continue. if (charlen <= width || matches[2] === ' ') { continue; } // The current charlen exceeds the width and a space wasn't matched. // "Roll everything back" until the last space character. tmp = captured.lastIndexOf(' '); text = captured.slice(tmp === -1 ? tmp : tmp + 1).join('') + text; captured = captured.slice(0, tmp); } // The limit has been reached. Push captured string onto result array. result.push(captured.join('')); // Reset captured array and charlen. captured = []; charlen = 0; } result.push(captured.join('')); return result.join('\n'); }; // todo: write unit tests // // function logs(text) { // [4, 6, 10, 15, 20, 25, 30, 40].forEach(function(n) { // log(n, text); // }); // } // // function log(n, text) { // console.log(Array(n + 1).join('-')); // console.log(wrap(n, text)); // } // // var text = 'this is '.red + 'a simple'.yellow.inverse + ' test of'.green + ' ' + 'some wrapped'.blue + ' text over '.inverse.magenta + 'many lines'.red; // logs(text); // // var text = 'foolish '.red.inverse + 'monkeys'.yellow + ' eating'.green + ' ' + 'delicious'.inverse.blue + ' bananas '.magenta + 'forever'.red; // logs(text); // // var text = 'foolish monkeys eating delicious bananas forever'.rainbow; // logs(text); // Format output into columns, wrapping words as-necessary. exports.table = function(widths, texts) { var rows = []; widths.forEach(function(width, i) { var lines = exports.wraptext(width, texts[i]).split('\n'); lines.forEach(function(line, j) { var row = rows[j]; if (!row) { row = rows[j] = []; } row[i] = line; }); }); var lines = []; rows.forEach(function(row) { var txt = ''; var column; for (var i = 0; i < row.length; i++) { column = row[i] || ''; txt += column; var diff = widths[i] - exports.uncolor(column).length; if (diff > 0) { txt += util.repeat(diff, ' '); } } lines.push(txt); }); return lines.join('\n'); };