debug.js
  1  
  2  /**
  3   * This is the common logic for both the Node.js and web browser
  4   * implementations of `debug()`.
  5   *
  6   * Expose `debug()` as the module.
  7   */
  8  
  9  exports = module.exports = createDebug.debug = createDebug['default'] = createDebug;
 10  exports.coerce = coerce;
 11  exports.disable = disable;
 12  exports.enable = enable;
 13  exports.enabled = enabled;
 14  exports.humanize = require('ms');
 15  
 16  /**
 17   * The currently active debug mode names, and names to skip.
 18   */
 19  
 20  exports.names = [];
 21  exports.skips = [];
 22  
 23  /**
 24   * Map of special "%n" handling functions, for the debug "format" argument.
 25   *
 26   * Valid key names are a single, lower or upper-case letter, i.e. "n" and "N".
 27   */
 28  
 29  exports.formatters = {};
 30  
 31  /**
 32   * Previous log timestamp.
 33   */
 34  
 35  var prevTime;
 36  
 37  /**
 38   * Select a color.
 39   * @param {String} namespace
 40   * @return {Number}
 41   * @api private
 42   */
 43  
 44  function selectColor(namespace) {
 45    var hash = 0, i;
 46  
 47    for (i in namespace) {
 48      hash  = ((hash << 5) - hash) + namespace.charCodeAt(i);
 49      hash |= 0; // Convert to 32bit integer
 50    }
 51  
 52    return exports.colors[Math.abs(hash) % exports.colors.length];
 53  }
 54  
 55  /**
 56   * Create a debugger with the given `namespace`.
 57   *
 58   * @param {String} namespace
 59   * @return {Function}
 60   * @api public
 61   */
 62  
 63  function createDebug(namespace) {
 64  
 65    function debug() {
 66      // disabled?
 67      if (!debug.enabled) return;
 68  
 69      var self = debug;
 70  
 71      // set `diff` timestamp
 72      var curr = +new Date();
 73      var ms = curr - (prevTime || curr);
 74      self.diff = ms;
 75      self.prev = prevTime;
 76      self.curr = curr;
 77      prevTime = curr;
 78  
 79      // turn the `arguments` into a proper Array
 80      var args = new Array(arguments.length);
 81      for (var i = 0; i < args.length; i++) {
 82        args[i] = arguments[i];
 83      }
 84  
 85      args[0] = exports.coerce(args[0]);
 86  
 87      if ('string' !== typeof args[0]) {
 88        // anything else let's inspect with %O
 89        args.unshift('%O');
 90      }
 91  
 92      // apply any `formatters` transformations
 93      var index = 0;
 94      args[0] = args[0].replace(/%([a-zA-Z%])/g, function(match, format) {
 95        // if we encounter an escaped % then don't increase the array index
 96        if (match === '%%') return match;
 97        index++;
 98        var formatter = exports.formatters[format];
 99        if ('function' === typeof formatter) {
100          var val = args[index];
101          match = formatter.call(self, val);
102  
103          // now we need to remove `args[index]` since it's inlined in the `format`
104          args.splice(index, 1);
105          index--;
106        }
107        return match;
108      });
109  
110      // apply env-specific formatting (colors, etc.)
111      exports.formatArgs.call(self, args);
112  
113      var logFn = debug.log || exports.log || console.log.bind(console);
114      logFn.apply(self, args);
115    }
116  
117    debug.namespace = namespace;
118    debug.enabled = exports.enabled(namespace);
119    debug.useColors = exports.useColors();
120    debug.color = selectColor(namespace);
121  
122    // env-specific initialization logic for debug instances
123    if ('function' === typeof exports.init) {
124      exports.init(debug);
125    }
126  
127    return debug;
128  }
129  
130  /**
131   * Enables a debug mode by namespaces. This can include modes
132   * separated by a colon and wildcards.
133   *
134   * @param {String} namespaces
135   * @api public
136   */
137  
138  function enable(namespaces) {
139    exports.save(namespaces);
140  
141    exports.names = [];
142    exports.skips = [];
143  
144    var split = (typeof namespaces === 'string' ? namespaces : '').split(/[\s,]+/);
145    var len = split.length;
146  
147    for (var i = 0; i < len; i++) {
148      if (!split[i]) continue; // ignore empty strings
149      namespaces = split[i].replace(/\*/g, '.*?');
150      if (namespaces[0] === '-') {
151        exports.skips.push(new RegExp('^' + namespaces.substr(1) + '$'));
152      } else {
153        exports.names.push(new RegExp('^' + namespaces + '$'));
154      }
155    }
156  }
157  
158  /**
159   * Disable debug output.
160   *
161   * @api public
162   */
163  
164  function disable() {
165    exports.enable('');
166  }
167  
168  /**
169   * Returns true if the given mode name is enabled, false otherwise.
170   *
171   * @param {String} name
172   * @return {Boolean}
173   * @api public
174   */
175  
176  function enabled(name) {
177    var i, len;
178    for (i = 0, len = exports.skips.length; i < len; i++) {
179      if (exports.skips[i].test(name)) {
180        return false;
181      }
182    }
183    for (i = 0, len = exports.names.length; i < len; i++) {
184      if (exports.names[i].test(name)) {
185        return true;
186      }
187    }
188    return false;
189  }
190  
191  /**
192   * Coerce `val`.
193   *
194   * @param {Mixed} val
195   * @return {Mixed}
196   * @api private
197   */
198  
199  function coerce(val) {
200    if (val instanceof Error) return val.stack || val.message;
201    return val;
202  }