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