common.js 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. /**
  2. * This is the common logic for both the Node.js and web browser
  3. * implementations of `debug()`.
  4. */
  5. function setup(env) {
  6. createDebug.debug = createDebug;
  7. createDebug.default = createDebug;
  8. createDebug.coerce = coerce;
  9. createDebug.disable = disable;
  10. createDebug.enable = enable;
  11. createDebug.enabled = enabled;
  12. createDebug.humanize = require('ms');
  13. createDebug.destroy = destroy;
  14. Object.keys(env).forEach(key => {
  15. createDebug[key] = env[key];
  16. });
  17. /**
  18. * The currently active debug mode names, and names to skip.
  19. */
  20. createDebug.names = [];
  21. createDebug.skips = [];
  22. /**
  23. * Map of special "%n" handling functions, for the debug "format" argument.
  24. *
  25. * Valid key names are a single, lower or upper-case letter, i.e. "n" and "N".
  26. */
  27. createDebug.formatters = {};
  28. /**
  29. * Selects a color for a debug namespace
  30. * @param {String} namespace The namespace string for the debug instance to be colored
  31. * @return {Number|String} An ANSI color code for the given namespace
  32. * @api private
  33. */
  34. function selectColor(namespace) {
  35. let hash = 0;
  36. for (let i = 0; i < namespace.length; i++) {
  37. hash = ((hash << 5) - hash) + namespace.charCodeAt(i);
  38. hash |= 0; // Convert to 32bit integer
  39. }
  40. return createDebug.colors[Math.abs(hash) % createDebug.colors.length];
  41. }
  42. createDebug.selectColor = selectColor;
  43. /**
  44. * Create a debugger with the given `namespace`.
  45. *
  46. * @param {String} namespace
  47. * @return {Function}
  48. * @api public
  49. */
  50. function createDebug(namespace) {
  51. let prevTime;
  52. let enableOverride = null;
  53. let namespacesCache;
  54. let enabledCache;
  55. function debug(...args) {
  56. // Disabled?
  57. if (!debug.enabled) {
  58. return;
  59. }
  60. const self = debug;
  61. // Set `diff` timestamp
  62. const curr = Number(new Date());
  63. const ms = curr - (prevTime || curr);
  64. self.diff = ms;
  65. self.prev = prevTime;
  66. self.curr = curr;
  67. prevTime = curr;
  68. args[0] = createDebug.coerce(args[0]);
  69. if (typeof args[0] !== 'string') {
  70. // Anything else let's inspect with %O
  71. args.unshift('%O');
  72. }
  73. // Apply any `formatters` transformations
  74. let index = 0;
  75. args[0] = args[0].replace(/%([a-zA-Z%])/g, (match, format) => {
  76. // If we encounter an escaped % then don't increase the array index
  77. if (match === '%%') {
  78. return '%';
  79. }
  80. index++;
  81. const formatter = createDebug.formatters[format];
  82. if (typeof formatter === 'function') {
  83. const val = args[index];
  84. match = formatter.call(self, val);
  85. // Now we need to remove `args[index]` since it's inlined in the `format`
  86. args.splice(index, 1);
  87. index--;
  88. }
  89. return match;
  90. });
  91. // Apply env-specific formatting (colors, etc.)
  92. createDebug.formatArgs.call(self, args);
  93. const logFn = self.log || createDebug.log;
  94. logFn.apply(self, args);
  95. }
  96. debug.namespace = namespace;
  97. debug.useColors = createDebug.useColors();
  98. debug.color = createDebug.selectColor(namespace);
  99. debug.extend = extend;
  100. debug.destroy = createDebug.destroy; // XXX Temporary. Will be removed in the next major release.
  101. Object.defineProperty(debug, 'enabled', {
  102. enumerable: true,
  103. configurable: false,
  104. get: () => {
  105. if (enableOverride !== null) {
  106. return enableOverride;
  107. }
  108. if (namespacesCache !== createDebug.namespaces) {
  109. namespacesCache = createDebug.namespaces;
  110. enabledCache = createDebug.enabled(namespace);
  111. }
  112. return enabledCache;
  113. },
  114. set: v => {
  115. enableOverride = v;
  116. }
  117. });
  118. // Env-specific initialization logic for debug instances
  119. if (typeof createDebug.init === 'function') {
  120. createDebug.init(debug);
  121. }
  122. return debug;
  123. }
  124. function extend(namespace, delimiter) {
  125. const newDebug = createDebug(this.namespace + (typeof delimiter === 'undefined' ? ':' : delimiter) + namespace);
  126. newDebug.log = this.log;
  127. return newDebug;
  128. }
  129. /**
  130. * Enables a debug mode by namespaces. This can include modes
  131. * separated by a colon and wildcards.
  132. *
  133. * @param {String} namespaces
  134. * @api public
  135. */
  136. function enable(namespaces) {
  137. createDebug.save(namespaces);
  138. createDebug.namespaces = namespaces;
  139. createDebug.names = [];
  140. createDebug.skips = [];
  141. const split = (typeof namespaces === 'string' ? namespaces : '')
  142. .trim()
  143. .replace(' ', ',')
  144. .split(',')
  145. .filter(Boolean);
  146. for (const ns of split) {
  147. if (ns[0] === '-') {
  148. createDebug.skips.push(ns.slice(1));
  149. } else {
  150. createDebug.names.push(ns);
  151. }
  152. }
  153. }
  154. /**
  155. * Checks if the given string matches a namespace template, honoring
  156. * asterisks as wildcards.
  157. *
  158. * @param {String} search
  159. * @param {String} template
  160. * @return {Boolean}
  161. */
  162. function matchesTemplate(search, template) {
  163. let searchIndex = 0;
  164. let templateIndex = 0;
  165. let starIndex = -1;
  166. let matchIndex = 0;
  167. while (searchIndex < search.length) {
  168. if (templateIndex < template.length && (template[templateIndex] === search[searchIndex] || template[templateIndex] === '*')) {
  169. // Match character or proceed with wildcard
  170. if (template[templateIndex] === '*') {
  171. starIndex = templateIndex;
  172. matchIndex = searchIndex;
  173. templateIndex++; // Skip the '*'
  174. } else {
  175. searchIndex++;
  176. templateIndex++;
  177. }
  178. } else if (starIndex !== -1) { // eslint-disable-line no-negated-condition
  179. // Backtrack to the last '*' and try to match more characters
  180. templateIndex = starIndex + 1;
  181. matchIndex++;
  182. searchIndex = matchIndex;
  183. } else {
  184. return false; // No match
  185. }
  186. }
  187. // Handle trailing '*' in template
  188. while (templateIndex < template.length && template[templateIndex] === '*') {
  189. templateIndex++;
  190. }
  191. return templateIndex === template.length;
  192. }
  193. /**
  194. * Disable debug output.
  195. *
  196. * @return {String} namespaces
  197. * @api public
  198. */
  199. function disable() {
  200. const namespaces = [
  201. ...createDebug.names,
  202. ...createDebug.skips.map(namespace => '-' + namespace)
  203. ].join(',');
  204. createDebug.enable('');
  205. return namespaces;
  206. }
  207. /**
  208. * Returns true if the given mode name is enabled, false otherwise.
  209. *
  210. * @param {String} name
  211. * @return {Boolean}
  212. * @api public
  213. */
  214. function enabled(name) {
  215. for (const skip of createDebug.skips) {
  216. if (matchesTemplate(name, skip)) {
  217. return false;
  218. }
  219. }
  220. for (const ns of createDebug.names) {
  221. if (matchesTemplate(name, ns)) {
  222. return true;
  223. }
  224. }
  225. return false;
  226. }
  227. /**
  228. * Coerce `val`.
  229. *
  230. * @param {Mixed} val
  231. * @return {Mixed}
  232. * @api private
  233. */
  234. function coerce(val) {
  235. if (val instanceof Error) {
  236. return val.stack || val.message;
  237. }
  238. return val;
  239. }
  240. /**
  241. * XXX DO NOT USE. This is a temporary stub function.
  242. * XXX It WILL be removed in the next major release.
  243. */
  244. function destroy() {
  245. console.warn('Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.');
  246. }
  247. createDebug.enable(createDebug.load());
  248. return createDebug;
  249. }
  250. module.exports = setup;