log.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. var util = require('util');
  2. var events = require('events');
  3. var _ = require('lodash');
  4. var table = require('text-table');
  5. var chalk = require('chalk');
  6. // padding step
  7. var step = ' ';
  8. var padding = ' ';
  9. // color -> status mappings
  10. var colors = {
  11. skip : 'yellow',
  12. force : 'yellow',
  13. create : 'green',
  14. invoke : 'bold',
  15. conflict : 'red',
  16. identical : 'cyan',
  17. info : 'gray'
  18. };
  19. function pad(status) {
  20. var max = 'identical'.length;
  21. var delta = max - status.length;
  22. return delta ? new Array(delta + 1).join(' ') + status : status;
  23. }
  24. // borrowed from https://github.com/mikeal/logref/blob/master/main.js#L6-15
  25. function formatter(msg, ctx) {
  26. while (msg.indexOf('%') !== -1) {
  27. var start = msg.indexOf('%');
  28. var end = msg.indexOf(' ', start);
  29. if (end === -1) {
  30. end = msg.length;
  31. }
  32. msg = msg.slice(0, start) + ctx[msg.slice(start + 1, end)] + msg.slice(end);
  33. }
  34. return msg;
  35. }
  36. module.exports = function logger() {
  37. // `this.log` is a [logref](https://github.com/mikeal/logref)
  38. // compatible logger, with an enhanced API.
  39. //
  40. // It also has EventEmitter like capabilities, so you can call on / emit
  41. // on it, namely used to increase or decrease the padding.
  42. //
  43. // All logs are done against STDERR, letting you stdout for meaningfull
  44. // value and redirection, should you need to generate output this way.
  45. //
  46. // Log functions take two arguments, a message and a context. For any
  47. // other kind of paramters, `console.error` is used, so all of the
  48. // console format string goodies you're used to work fine.
  49. //
  50. // - msg - The message to show up
  51. // - context - The optional context to escape the message against
  52. //
  53. // Retunrns the logger
  54. function log(msg, ctx) {
  55. msg = msg || '';
  56. if (!ctx) {
  57. ctx = {};
  58. }
  59. if (typeof ctx === 'object' && !Array.isArray(ctx)) {
  60. console.error(formatter(msg, ctx));
  61. } else {
  62. console.error.apply(console, arguments);
  63. }
  64. return log;
  65. }
  66. _.extend(log, events.EventEmitter.prototype);
  67. // A simple write method, with formatted message. If `msg` is
  68. // ommitted, then a single `\n` is written.
  69. //
  70. // Returns the logger
  71. log.write = function (msg) {
  72. if (!msg) {
  73. return this.write('\n');
  74. }
  75. process.stderr.write(util.format.apply(util, arguments));
  76. return this;
  77. };
  78. // Same as `log.write()` but automatically appends a `\n` at the end
  79. // of the message.
  80. log.writeln = function () {
  81. return this.write.apply(this, arguments).write();
  82. };
  83. // Convenience helper to write sucess status, this simply prepends the
  84. // message with a gren `✔`.
  85. log.ok = function (msg) {
  86. this.write(chalk.green('✔ ') + util.format.apply(util, arguments) + '\n');
  87. return this;
  88. };
  89. log.error = function (msg) {
  90. this.write(chalk.red('✗ ') + util.format.apply(util, arguments) + '\n');
  91. return this;
  92. };
  93. log.on('up', function () {
  94. padding = padding + step;
  95. });
  96. log.on('down', function () {
  97. padding = padding.replace(step, '');
  98. });
  99. Object.keys(colors).forEach(function (status) {
  100. // Each predefined status has its logging method utility, handling
  101. // status color and padding before the usual `.write()`
  102. //
  103. // Example
  104. //
  105. // this.log
  106. // .write()
  107. // .info('Doing something')
  108. // .force('Forcing filepath %s, 'some path')
  109. // .conflict('on %s' 'model.js')
  110. // .write()
  111. // .ok('This is ok');
  112. //
  113. // The list of status and mapping colors
  114. //
  115. // skip yellow
  116. // force yellow
  117. // create green
  118. // invoke bold
  119. // conflict red
  120. // identical cyan
  121. // info grey
  122. //
  123. // Returns the logger
  124. log[status] = function () {
  125. var color = colors[status];
  126. this.write(chalk[color](pad(status))).write(padding);
  127. this.write(util.format.apply(util, arguments) + '\n');
  128. return this;
  129. };
  130. });
  131. // A basic wrapper around `cli-table` package, resetting any single
  132. // char to empty strings, this is used for aligning options and
  133. // arguments without too much Math on our side.
  134. //
  135. // - opts - A list of rows or an Hash of options to pass through cli
  136. // table.
  137. //
  138. // Returns the table reprensetation
  139. log.table = function (opts) {
  140. var tableData = [];
  141. opts = Array.isArray(opts) ? { rows: opts } : opts;
  142. opts.rows = opts.rows || [];
  143. opts.rows.forEach(function (row) {
  144. tableData.push(row);
  145. });
  146. return table(tableData);
  147. };
  148. return log;
  149. };