util.js (15563B)
1 // Copyright Joyent, Inc. and other Node contributors. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a 4 // copy of this software and associated documentation files (the 5 // "Software"), to deal in the Software without restriction, including 6 // without limitation the rights to use, copy, modify, merge, publish, 7 // distribute, sublicense, and/or sell copies of the Software, and to permit 8 // persons to whom the Software is furnished to do so, subject to the 9 // following conditions: 10 // 11 // The above copyright notice and this permission notice shall be included 12 // in all copies or substantial portions of the Software. 13 // 14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 // USE OR OTHER DEALINGS IN THE SOFTWARE. 21 22 var formatRegExp = /%[sdj%]/g; 23 exports.format = function(f) { 24 if (!isString(f)) { 25 var objects = []; 26 for (var i = 0; i < arguments.length; i++) { 27 objects.push(inspect(arguments[i])); 28 } 29 return objects.join(' '); 30 } 31 32 var i = 1; 33 var args = arguments; 34 var len = args.length; 35 var str = String(f).replace(formatRegExp, function(x) { 36 if (x === '%%') return '%'; 37 if (i >= len) return x; 38 switch (x) { 39 case '%s': return String(args[i++]); 40 case '%d': return Number(args[i++]); 41 case '%j': 42 try { 43 return JSON.stringify(args[i++]); 44 } catch (_) { 45 return '[Circular]'; 46 } 47 default: 48 return x; 49 } 50 }); 51 for (var x = args[i]; i < len; x = args[++i]) { 52 if (isNull(x) || !isObject(x)) { 53 str += ' ' + x; 54 } else { 55 str += ' ' + inspect(x); 56 } 57 } 58 return str; 59 }; 60 61 62 // Mark that a method should not be used. 63 // Returns a modified function which warns once by default. 64 // If --no-deprecation is set, then it is a no-op. 65 exports.deprecate = function(fn, msg) { 66 // Allow for deprecating things in the process of starting up. 67 if (isUndefined(global.process)) { 68 return function() { 69 return exports.deprecate(fn, msg).apply(this, arguments); 70 }; 71 } 72 73 if (process.noDeprecation === true) { 74 return fn; 75 } 76 77 var warned = false; 78 function deprecated() { 79 if (!warned) { 80 if (process.throwDeprecation) { 81 throw new Error(msg); 82 } else if (process.traceDeprecation) { 83 console.trace(msg); 84 } else { 85 console.error(msg); 86 } 87 warned = true; 88 } 89 return fn.apply(this, arguments); 90 } 91 92 return deprecated; 93 }; 94 95 96 var debugs = {}; 97 var debugEnviron; 98 exports.debuglog = function(set) { 99 if (isUndefined(debugEnviron)) 100 debugEnviron = process.env.NODE_DEBUG || ''; 101 set = set.toUpperCase(); 102 if (!debugs[set]) { 103 if (new RegExp('\\b' + set + '\\b', 'i').test(debugEnviron)) { 104 var pid = process.pid; 105 debugs[set] = function() { 106 var msg = exports.format.apply(exports, arguments); 107 console.error('%s %d: %s', set, pid, msg); 108 }; 109 } else { 110 debugs[set] = function() {}; 111 } 112 } 113 return debugs[set]; 114 }; 115 116 117 /** 118 * Echos the value of a value. Trys to print the value out 119 * in the best way possible given the different types. 120 * 121 * @param {Object} obj The object to print out. 122 * @param {Object} opts Optional options object that alters the output. 123 */ 124 /* legacy: obj, showHidden, depth, colors*/ 125 function inspect(obj, opts) { 126 // default options 127 var ctx = { 128 seen: [], 129 stylize: stylizeNoColor 130 }; 131 // legacy... 132 if (arguments.length >= 3) ctx.depth = arguments[2]; 133 if (arguments.length >= 4) ctx.colors = arguments[3]; 134 if (isBoolean(opts)) { 135 // legacy... 136 ctx.showHidden = opts; 137 } else if (opts) { 138 // got an "options" object 139 exports._extend(ctx, opts); 140 } 141 // set default options 142 if (isUndefined(ctx.showHidden)) ctx.showHidden = false; 143 if (isUndefined(ctx.depth)) ctx.depth = 2; 144 if (isUndefined(ctx.colors)) ctx.colors = false; 145 if (isUndefined(ctx.customInspect)) ctx.customInspect = true; 146 if (ctx.colors) ctx.stylize = stylizeWithColor; 147 return formatValue(ctx, obj, ctx.depth); 148 } 149 exports.inspect = inspect; 150 151 152 // http://en.wikipedia.org/wiki/ANSI_escape_code#graphics 153 inspect.colors = { 154 'bold' : [1, 22], 155 'italic' : [3, 23], 156 'underline' : [4, 24], 157 'inverse' : [7, 27], 158 'white' : [37, 39], 159 'grey' : [90, 39], 160 'black' : [30, 39], 161 'blue' : [34, 39], 162 'cyan' : [36, 39], 163 'green' : [32, 39], 164 'magenta' : [35, 39], 165 'red' : [31, 39], 166 'yellow' : [33, 39] 167 }; 168 169 // Don't use 'blue' not visible on cmd.exe 170 inspect.styles = { 171 'special': 'cyan', 172 'number': 'yellow', 173 'boolean': 'yellow', 174 'undefined': 'grey', 175 'null': 'bold', 176 'string': 'green', 177 'date': 'magenta', 178 // "name": intentionally not styling 179 'regexp': 'red' 180 }; 181 182 183 function stylizeWithColor(str, styleType) { 184 var style = inspect.styles[styleType]; 185 186 if (style) { 187 return '\u001b[' + inspect.colors[style][0] + 'm' + str + 188 '\u001b[' + inspect.colors[style][1] + 'm'; 189 } else { 190 return str; 191 } 192 } 193 194 195 function stylizeNoColor(str, styleType) { 196 return str; 197 } 198 199 200 function arrayToHash(array) { 201 var hash = {}; 202 203 array.forEach(function(val, idx) { 204 hash[val] = true; 205 }); 206 207 return hash; 208 } 209 210 211 function formatValue(ctx, value, recurseTimes) { 212 // Provide a hook for user-specified inspect functions. 213 // Check that value is an object with an inspect function on it 214 if (ctx.customInspect && 215 value && 216 isFunction(value.inspect) && 217 // Filter out the util module, it's inspect function is special 218 value.inspect !== exports.inspect && 219 // Also filter out any prototype objects using the circular check. 220 !(value.constructor && value.constructor.prototype === value)) { 221 var ret = value.inspect(recurseTimes, ctx); 222 if (!isString(ret)) { 223 ret = formatValue(ctx, ret, recurseTimes); 224 } 225 return ret; 226 } 227 228 // Primitive types cannot have properties 229 var primitive = formatPrimitive(ctx, value); 230 if (primitive) { 231 return primitive; 232 } 233 234 // Look up the keys of the object. 235 var keys = Object.keys(value); 236 var visibleKeys = arrayToHash(keys); 237 238 if (ctx.showHidden) { 239 keys = Object.getOwnPropertyNames(value); 240 } 241 242 // IE doesn't make error fields non-enumerable 243 // http://msdn.microsoft.com/en-us/library/ie/dww52sbt(v=vs.94).aspx 244 if (isError(value) 245 && (keys.indexOf('message') >= 0 || keys.indexOf('description') >= 0)) { 246 return formatError(value); 247 } 248 249 // Some type of object without properties can be shortcutted. 250 if (keys.length === 0) { 251 if (isFunction(value)) { 252 var name = value.name ? ': ' + value.name : ''; 253 return ctx.stylize('[Function' + name + ']', 'special'); 254 } 255 if (isRegExp(value)) { 256 return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); 257 } 258 if (isDate(value)) { 259 return ctx.stylize(Date.prototype.toString.call(value), 'date'); 260 } 261 if (isError(value)) { 262 return formatError(value); 263 } 264 } 265 266 var base = '', array = false, braces = ['{', '}']; 267 268 // Make Array say that they are Array 269 if (isArray(value)) { 270 array = true; 271 braces = ['[', ']']; 272 } 273 274 // Make functions say that they are functions 275 if (isFunction(value)) { 276 var n = value.name ? ': ' + value.name : ''; 277 base = ' [Function' + n + ']'; 278 } 279 280 // Make RegExps say that they are RegExps 281 if (isRegExp(value)) { 282 base = ' ' + RegExp.prototype.toString.call(value); 283 } 284 285 // Make dates with properties first say the date 286 if (isDate(value)) { 287 base = ' ' + Date.prototype.toUTCString.call(value); 288 } 289 290 // Make error with message first say the error 291 if (isError(value)) { 292 base = ' ' + formatError(value); 293 } 294 295 if (keys.length === 0 && (!array || value.length == 0)) { 296 return braces[0] + base + braces[1]; 297 } 298 299 if (recurseTimes < 0) { 300 if (isRegExp(value)) { 301 return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); 302 } else { 303 return ctx.stylize('[Object]', 'special'); 304 } 305 } 306 307 ctx.seen.push(value); 308 309 var output; 310 if (array) { 311 output = formatArray(ctx, value, recurseTimes, visibleKeys, keys); 312 } else { 313 output = keys.map(function(key) { 314 return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array); 315 }); 316 } 317 318 ctx.seen.pop(); 319 320 return reduceToSingleString(output, base, braces); 321 } 322 323 324 function formatPrimitive(ctx, value) { 325 if (isUndefined(value)) 326 return ctx.stylize('undefined', 'undefined'); 327 if (isString(value)) { 328 var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '') 329 .replace(/'/g, "\\'") 330 .replace(/\\"/g, '"') + '\''; 331 return ctx.stylize(simple, 'string'); 332 } 333 if (isNumber(value)) 334 return ctx.stylize('' + value, 'number'); 335 if (isBoolean(value)) 336 return ctx.stylize('' + value, 'boolean'); 337 // For some reason typeof null is "object", so special case here. 338 if (isNull(value)) 339 return ctx.stylize('null', 'null'); 340 } 341 342 343 function formatError(value) { 344 return '[' + Error.prototype.toString.call(value) + ']'; 345 } 346 347 348 function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { 349 var output = []; 350 for (var i = 0, l = value.length; i < l; ++i) { 351 if (hasOwnProperty(value, String(i))) { 352 output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, 353 String(i), true)); 354 } else { 355 output.push(''); 356 } 357 } 358 keys.forEach(function(key) { 359 if (!key.match(/^\d+$/)) { 360 output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, 361 key, true)); 362 } 363 }); 364 return output; 365 } 366 367 368 function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) { 369 var name, str, desc; 370 desc = Object.getOwnPropertyDescriptor(value, key) || { value: value[key] }; 371 if (desc.get) { 372 if (desc.set) { 373 str = ctx.stylize('[Getter/Setter]', 'special'); 374 } else { 375 str = ctx.stylize('[Getter]', 'special'); 376 } 377 } else { 378 if (desc.set) { 379 str = ctx.stylize('[Setter]', 'special'); 380 } 381 } 382 if (!hasOwnProperty(visibleKeys, key)) { 383 name = '[' + key + ']'; 384 } 385 if (!str) { 386 if (ctx.seen.indexOf(desc.value) < 0) { 387 if (isNull(recurseTimes)) { 388 str = formatValue(ctx, desc.value, null); 389 } else { 390 str = formatValue(ctx, desc.value, recurseTimes - 1); 391 } 392 if (str.indexOf('\n') > -1) { 393 if (array) { 394 str = str.split('\n').map(function(line) { 395 return ' ' + line; 396 }).join('\n').substr(2); 397 } else { 398 str = '\n' + str.split('\n').map(function(line) { 399 return ' ' + line; 400 }).join('\n'); 401 } 402 } 403 } else { 404 str = ctx.stylize('[Circular]', 'special'); 405 } 406 } 407 if (isUndefined(name)) { 408 if (array && key.match(/^\d+$/)) { 409 return str; 410 } 411 name = JSON.stringify('' + key); 412 if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) { 413 name = name.substr(1, name.length - 2); 414 name = ctx.stylize(name, 'name'); 415 } else { 416 name = name.replace(/'/g, "\\'") 417 .replace(/\\"/g, '"') 418 .replace(/(^"|"$)/g, "'"); 419 name = ctx.stylize(name, 'string'); 420 } 421 } 422 423 return name + ': ' + str; 424 } 425 426 427 function reduceToSingleString(output, base, braces) { 428 var numLinesEst = 0; 429 var length = output.reduce(function(prev, cur) { 430 numLinesEst++; 431 if (cur.indexOf('\n') >= 0) numLinesEst++; 432 return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1; 433 }, 0); 434 435 if (length > 60) { 436 return braces[0] + 437 (base === '' ? '' : base + '\n ') + 438 ' ' + 439 output.join(',\n ') + 440 ' ' + 441 braces[1]; 442 } 443 444 return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1]; 445 } 446 447 448 // NOTE: These type checking functions intentionally don't use `instanceof` 449 // because it is fragile and can be easily faked with `Object.create()`. 450 function isArray(ar) { 451 return Array.isArray(ar); 452 } 453 exports.isArray = isArray; 454 455 function isBoolean(arg) { 456 return typeof arg === 'boolean'; 457 } 458 exports.isBoolean = isBoolean; 459 460 function isNull(arg) { 461 return arg === null; 462 } 463 exports.isNull = isNull; 464 465 function isNullOrUndefined(arg) { 466 return arg == null; 467 } 468 exports.isNullOrUndefined = isNullOrUndefined; 469 470 function isNumber(arg) { 471 return typeof arg === 'number'; 472 } 473 exports.isNumber = isNumber; 474 475 function isString(arg) { 476 return typeof arg === 'string'; 477 } 478 exports.isString = isString; 479 480 function isSymbol(arg) { 481 return typeof arg === 'symbol'; 482 } 483 exports.isSymbol = isSymbol; 484 485 function isUndefined(arg) { 486 return arg === void 0; 487 } 488 exports.isUndefined = isUndefined; 489 490 function isRegExp(re) { 491 return isObject(re) && objectToString(re) === '[object RegExp]'; 492 } 493 exports.isRegExp = isRegExp; 494 495 function isObject(arg) { 496 return typeof arg === 'object' && arg !== null; 497 } 498 exports.isObject = isObject; 499 500 function isDate(d) { 501 return isObject(d) && objectToString(d) === '[object Date]'; 502 } 503 exports.isDate = isDate; 504 505 function isError(e) { 506 return isObject(e) && 507 (objectToString(e) === '[object Error]' || e instanceof Error); 508 } 509 exports.isError = isError; 510 511 function isFunction(arg) { 512 return typeof arg === 'function'; 513 } 514 exports.isFunction = isFunction; 515 516 function isPrimitive(arg) { 517 return arg === null || 518 typeof arg === 'boolean' || 519 typeof arg === 'number' || 520 typeof arg === 'string' || 521 typeof arg === 'symbol' || // ES6 symbol 522 typeof arg === 'undefined'; 523 } 524 exports.isPrimitive = isPrimitive; 525 526 exports.isBuffer = require('./support/isBuffer'); 527 528 function objectToString(o) { 529 return Object.prototype.toString.call(o); 530 } 531 532 533 function pad(n) { 534 return n < 10 ? '0' + n.toString(10) : n.toString(10); 535 } 536 537 538 var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 539 'Oct', 'Nov', 'Dec']; 540 541 // 26 Feb 16:19:34 542 function timestamp() { 543 var d = new Date(); 544 var time = [pad(d.getHours()), 545 pad(d.getMinutes()), 546 pad(d.getSeconds())].join(':'); 547 return [d.getDate(), months[d.getMonth()], time].join(' '); 548 } 549 550 551 // log is just a thin wrapper to console.log that prepends a timestamp 552 exports.log = function() { 553 console.log('%s - %s', timestamp(), exports.format.apply(exports, arguments)); 554 }; 555 556 557 /** 558 * Inherit the prototype methods from one constructor into another. 559 * 560 * The Function.prototype.inherits from lang.js rewritten as a standalone 561 * function (not on Function.prototype). NOTE: If this file is to be loaded 562 * during bootstrapping this function needs to be rewritten using some native 563 * functions as prototype setup using normal JavaScript does not work as 564 * expected during bootstrapping (see mirror.js in r114903). 565 * 566 * @param {function} ctor Constructor function which needs to inherit the 567 * prototype. 568 * @param {function} superCtor Constructor function to inherit prototype from. 569 */ 570 exports.inherits = require('inherits'); 571 572 exports._extend = function(origin, add) { 573 // Don't do anything if add isn't an object 574 if (!add || !isObject(add)) return origin; 575 576 var keys = Object.keys(add); 577 var i = keys.length; 578 while (i--) { 579 origin[keys[i]] = add[keys[i]]; 580 } 581 return origin; 582 }; 583 584 function hasOwnProperty(obj, prop) { 585 return Object.prototype.hasOwnProperty.call(obj, prop); 586 }